【问题标题】:Passing additional arguments to user control inside the data template将附加参数传递给数据模板内的用户控件
【发布时间】:2014-01-02 16:23:11
【问题描述】:

这是我正在使用的 xaml 代码

  <GridView
        Grid.Row="0"
        x:Name="RootGrid"
        SelectionMode="None"
        IsItemClickEnabled="True"
        ItemsSource="{Binding RootListSource}">

        <GridView.ItemTemplate>
            <DataTemplate>
                <UserControl:TreeInfoControl/>
            </DataTemplate>
        </GridView.ItemTemplate>

    </GridView>

在这个我的用户控件中,它包含另一个 GridView,它拥有不同的 IEnumerable 集合。我想要实现的是我需要通过代码传递这个集合。我通过向树控件添加依赖属性来尝试此操作,但它不起作用。所以我正在寻找一种能够通过xaml(以某种方式通过用户控件)传递集合的解决方案。我知道可以将该集合添加到我现有的集合中并绑定该集合。但现在我不能使用那种方法。

【问题讨论】:

  • 我也在寻找解决这个问题的方法。在 WPF 中是可能的
  • 很遗憾你不能使用有效的技术。

标签: xaml windows-8 windows-runtime windows-store-apps winrt-xaml


【解决方案1】:

这就是你的做法。

从您的 App.xaml 开始,以便我们可以重复使用演示模板

<Application.Resources>
    <DataTemplate x:Key="MyContentControl">
        <Grid Height="100" Width="100" Background="Maroon">
            <TextBlock Text="{Binding FallbackValue=0}" Foreground="White" FontSize="40" VerticalAlignment="Center" HorizontalAlignment="Center" />
        </Grid>
    </DataTemplate>
</Application.Resources>

然后我们可以定义你的用户控件

<d:UserControl.DataContext>
    <local:MyControlViewModel Number="-1" Letter="~K" />
</d:UserControl.DataContext>

<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left">
    <ContentControl Content="{Binding Number}" 
                    ContentTemplate="{StaticResource MyContentControl}" />
    <ListView ItemsSource="{Binding Letters}" IsHitTestVisible="False"
              ItemTemplate="{StaticResource MyContentControl}"
              SelectedItem="{Binding Letter, Mode=TwoWay}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <ItemsStackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ListView>
</StackPanel>

然后我们可以定义你的 MainPage.xaml

<Page.DataContext>
    <local:MainPageViewModel Letter="C" />
</Page.DataContext>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="140" />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <ListView x:Name="MyList" ItemsSource="{Binding Letters}" 
              ItemTemplate="{StaticResource MyContentControl}"
              SelectedItem="{Binding Letter, Mode=TwoWay}" />
    <ListView Grid.Column="1" ItemsSource="{Binding Numbers}">
        <ListView.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <StackPanel.Resources>
                        <local:MyControlViewModel 
                            x:Key="MyDataContext" Number="{Binding}" 
                            Letters="{Binding ItemsSource, ElementName=MyList}" 
                            Letter="{Binding SelectedItem, ElementName=MyList}" />
                    </StackPanel.Resources>
                    <local:MyControl DataContext="{StaticResource MyDataContext}" />
                </StackPanel>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

还没有什么特别的,对吧?嗯,没那么快。我们正在为用户控件创建视图模型,从周围范围设置视图模型的属性,然后将其显式传递给用户控件的 DataContext。酷吧?很简单,如果你考虑一下。想要在标签内设置这些属性?你当然知道。但你不能。操作顺序都是错误的。你只需要相信我。

现在,您的用户控件背后有零代码。但是视图模型看起来像这样:

public class MyControlViewModel : BindableBase
{
    public int Number
    {
        get { return (int)GetValue(NumberProperty); }
        set
        {
            SetValue(NumberProperty, value);
            base.RaisePropertyChanged();
        }
    }
    public static readonly DependencyProperty NumberProperty =
        DependencyProperty.Register("Number", typeof(int), typeof(MyControlViewModel),
        new PropertyMetadata(0, (s, e) => { }));

    public string Letter
    {
        get { return (string)GetValue(LetterProperty); }
        set
        {
            SetValue(LetterProperty, value);
            base.RaisePropertyChanged();
        }
    }
    public static readonly DependencyProperty LetterProperty =
        DependencyProperty.Register("Letter", typeof(string), typeof(MyControlViewModel),
        new PropertyMetadata("Z", (s, e) => { }));

    public ObservableCollection<string> Letters
    {
        get { return (ObservableCollection<string>)GetValue(LettersProperty); }
        set
        {
            SetValue(LettersProperty, value);
            base.RaisePropertyChanged();
        }
    }
    public static readonly DependencyProperty LettersProperty =
        DependencyProperty.Register("Letters", typeof(ObservableCollection<string>),
        typeof(MyControlViewModel),
        new PropertyMetadata(new ObservableCollection<string>(new[] { "~W", "~X", "~Y", "~Z" }), (s, e) => { }));
}

所有属性都是依赖属性。我希望你注意到了。我这样做不仅仅是因为我喜欢打字。虽然我喜欢打字。事实是,我这样做是因为为了拥有内部绑定,您必须使用依赖属性 - 以及引发属性更改的依赖属性!最后一部分不是微不足道的。但它必须在视图模型中吗?不。但我喜欢这样。

你可以参考这个:http://blog.jerrynixon.com/2013/07/solved-two-way-binding-inside-user.html

您的 MainPage 也没有任何代码。但是视图模型看起来像这样:

public class MainPageViewModel : BindableBase
{
    public MainPageViewModel()
    {
        this._Letters = new ObservableCollection<string>(new[] { "A", "B", "C", "D" });
        this._Numbers = new ObservableCollection<int>(new[] { 1, 2, 3, 4 });
    }

    public string Letter
    {
        get { return (string)GetValue(LetterProperty); }
        set
        {
            SetValue(LetterProperty, value);
            base.RaisePropertyChanged();
        }
    }
    public static readonly DependencyProperty LetterProperty =
        DependencyProperty.Register("Letter", typeof(string), typeof(MyControlViewModel),
        new PropertyMetadata("Z", (s, e) => { }));

    ObservableCollection<string> _Letters = new ObservableCollection<string>();
    public ObservableCollection<string> Letters { get { return _Letters; } }

    ObservableCollection<int> _Numbers = new ObservableCollection<int>();
    public ObservableCollection<int> Numbers { get { return _Numbers; } }
}

可绑定的基础是标准的,这是它的代码:

public abstract class BindableBase : DependencyObject, System.ComponentModel.INotifyPropertyChanged
{

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    protected void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
    {
        if (!object.Equals(storage, value))
        {
            storage = value;
            if (PropertyChanged != null)
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
    protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }
}

当一切都完成后,你应该得到你想要的。像这样的:

不要过度简化事情。但是,就这么简单。

看,当您开始嵌套上下文时,让您对 XAML 有所了解并不总是那么容易。我不怪你在第一次运行时没有得到它。但我希望这可以帮助您入门。继续推

祝你好运!

【讨论】:

  • 谢谢尼克松。这对我帮助很大。谢谢你的建议.. :)
猜你喜欢
  • 2014-03-31
  • 1970-01-01
  • 2011-07-03
  • 2010-12-15
  • 2014-11-27
  • 1970-01-01
  • 1970-01-01
  • 2011-02-12
  • 1970-01-01
相关资源
最近更新 更多