我有两个用户控件,一个包含TreeView
,一个包含ListView
。
TreeView
有一个itemsource和分层数据模板,用于填充节点和leafes(node = TvShow,leaf = Season)。
ListView
应显示所选TreeView项目的子项(因此,所选季节):该季节的剧集。
当我在同一个窗口中定义了TreeView和Listview时,这很好用,我可以使用这样的东西:
<ListView
x:Name="_listViewEpisodes"
Grid.Column="2"
ItemsSource="{Binding ElementName=_tvShowsTreeView, Path=SelectedItem.Episodes}">
如果在单独的用户控件中定义了两个控件,我该如何实现? (因为在一个用户控件的上下文中,我想念其他用户控件的上下文)
这似乎是非常基本的东西,我感到很沮丧,我自己无法弄明白。我拒绝用代码隐藏解决这个问题,到目前为止我有一个非常干净的MVVM项目,我想保持这种方式。
希望有人能给我一些建议!
答案 0 :(得分:2)
首先,您必须在ViewModel中创建SelectedValue proeprty并将TreeView.SelectedItem属性绑定到它。由于SelectedItem属性是只读的,我建议您创建一个帮助程序来创建类似OneWayToSource的绑定。代码应如下所示:
public class BindingWrapper {
public static object GetSource(DependencyObject obj) { return (object)obj.GetValue(SourceProperty); }
public static void SetSource(DependencyObject obj, object value) { obj.SetValue(SourceProperty, value); }
public static object GetTarget(DependencyObject obj) { return (object)obj.GetValue(TargetProperty); }
public static void SetTarget(DependencyObject obj, object value) { obj.SetValue(TargetProperty, value); }
public static readonly DependencyProperty TargetProperty = DependencyProperty.RegisterAttached("Target", typeof(object), typeof(BindingWrapper), new PropertyMetadata(null));
public static readonly DependencyProperty SourceProperty = DependencyProperty.RegisterAttached("Source", typeof(object), typeof(BindingWrapper), new PropertyMetadata(null, OnSourceChanged));
static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
SetTarget(d, e.NewValue);
}
}
这个想法很简单:你有两个附加属性,Source和Target。当第一个更改时,将调用PropertyChangedCallback,您只需将NewValue设置为Target属性值。在我看来,当您需要在XAML中绑定只读属性时(特别是在控件模板中),这种情况在很多情况下都很有用。
我已经创建了一个简单的模型来演示如何使用这个帮助器:
public class ViewModel : INotifyPropertyChanged {
public ViewModel() {
this.values = new ObservableCollection<string>()
{
"first",
"second",
"third"
};
}
ObservableCollection<string> values;
string selectedValue;
public ObservableCollection<string> Values { get { return values; } }
public string SelectedValue {
get { return selectedValue; }
set {
if (Equals(selectedValue, values))
return;
selectedValue = value;
if (PropertyChanged == null)
return;
PropertyChanged(this, new PropertyChangedEventArgs("SelectedValue"));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
所以,我们有数据源,选择的值,我们将这样绑定它:
<StackPanel>
<TreeView ItemsSource="{Binding Values}"
local:BindingWrapper.Source="{Binding SelectedItem, RelativeSource={RelativeSource Self}, Mode=OneWay}"
local:BindingWrapper.Target="{Binding SelectedValue, Mode=OneWayToSource}"
>
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="Header" Value="{Binding}"/>
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<TextBlock Text="{Binding SelectedValue}"/>
</StackPanel>
在从ViewModel绑定到ItemsSource的TreeView中,我创建了两个绑定,因此它们正在更改ViewModel中的SelectedValue属性。示例末尾的TextBlock仅用于表明此方法有效。
关于非常干净的MVVM - 我认为它与&#34;没有代码隐藏&#34;不同。在我的示例中,ViewModel仍然不了解您的视图,以及您是否会使用其他控件来显示您的数据,例如ListBox你将能够使用简单的双向绑定和&#34; BindingWrapper&#34;帮助程序不会使您的代码不可读或不可移植或其他任何东西。
答案 1 :(得分:0)
在ViewModel中创建一个SelectedSeason
属性,并将ListView的ItemsSource
绑定到SelectedSeason.Episodes
。
在一个完美的世界中,您现在可以在TreeView中使用双向绑定,以便在SelectedItem
更改时自动更新此属性。但是,TreeView的SelectedItem
属性是只读的,不能绑定。您可以使用一点代码隐藏,并为TreeView的SelectionChanged
事件创建一个事件处理程序,以更新ViewModel的SelectedSeason
。恕我直言,这并没有违反MVVM原则。
如果您需要纯XAML解决方案,请查看this answer。