Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/287.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用户控制依赖于TreeView';s(WPF)选择编辑项_C#_Wpf_Mvvm_Treeview_Selecteditem - Fatal编程技术网

C# 用户控制依赖于TreeView';s(WPF)选择编辑项

C# 用户控制依赖于TreeView';s(WPF)选择编辑项,c#,wpf,mvvm,treeview,selecteditem,C#,Wpf,Mvvm,Treeview,Selecteditem,我有两个用户控件,一个包含TreeView,一个包含ListView TreeView有一个itemsource和分层数据模板,用于填充节点和叶(node=TvShow,leaf=seasure) 列表视图应显示所选TreeView项目的子项(即所选季节):该季节的剧集 当我在同一个窗口中定义了TreeView和Listview时,这一切都很好,我可以使用如下内容: <ListView x:Name="_listViewEpisodes" Grid.Column="2"

我有两个用户控件,一个包含
TreeView
,一个包含
ListView

TreeView
有一个itemsource和分层数据模板,用于填充节点和叶(node=TvShow,leaf=seasure)

列表视图
应显示所选TreeView项目的子项(即所选季节):该季节的剧集

当我在同一个窗口中定义了TreeView和Listview时,这一切都很好,我可以使用如下内容:

<ListView
    x:Name="_listViewEpisodes"
    Grid.Column="2"
    ItemsSource="{Binding ElementName=_tvShowsTreeView, Path=SelectedItem.Episodes}">
    <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>

当两个控件都在单独的用户控件中定义时,如何实现这一点?(因为在一个用户控件的上下文中,我错过了另一个用户控件的上下文)

这似乎是一件非常基本的事情,我感到沮丧,因为我自己无法解决它。我拒绝用代码隐藏来解决这个问题,到目前为止,我有一个非常干净的MVVM项目,我希望保持这种方式


希望有人能给我一些建议

在ViewModel中创建一个
SelectedSeason
属性,并将ListView的
ItemsSource
绑定到
SelectedSeason.Spices

在一个完美的世界中,您现在可以在树视图中使用双向绑定,在
SelectedItem
更改时自动更新此属性。但是,TreeView的
SelectedItem
属性是只读的,无法绑定。您只需使用少量代码,就可以为TreeView的
SelectionChanged
事件创建一个事件处理程序,以更新ViewModel的
SelectedSeason
。我认为这不会违反MVVM原则


如果您想要一个纯XAML解决方案,那就看一看。

首先,您必须在ViewModel中创建SelectedValue项目,并将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);
    }
}
想法很简单:您有两个附加属性,源和目标。当第一个更改时,将调用PropertyChangedCallback,您只需将NewValue设置为目标属性值。在我看来,当您需要在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;
}
公共类视图模型:INotifyPropertyChanged{
公共视图模型(){
this.values=新的ObservableCollection()
{
“第一”,
“第二”,
“第三”
};
}
可观察收集值;
字符串selectedValue;
公共ObservableCollection值{get{return Values;}}
公共字符串SelectedValue{
获取{返回selectedValue;}
设置{
如果(等于(选择的值、值))
返回;
selectedValue=值;
if(PropertyChanged==null)
返回;
PropertyChanged(这是新的PropertyChangedEventArgs(“SelectedValue”);
}
}
公共事件属性更改事件处理程序属性更改;
}
所以,我们有数据源,选择值,我们将像这样绑定它:

<ListView
    x:Name="_listViewEpisodes"
    Grid.Column="2"
    ItemsSource="{Binding ElementName=_tvShowsTreeView, Path=SelectedItem.Episodes}">
    <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——我认为它与“无代码隐藏”不同。在我的示例中,ViewModel仍然对您的视图一无所知,如果您使用另一个控件来显示您的数据,例如ListBox,您将能够使用简单的双向绑定,“BindingWrapper”助手将不会使您的代码不可读或不可移植或其他任何内容。

您将其标记为MVVM,因此,将这两个控件绑定到VM上的SelectedEpisodes属性。正确的方向是将这两个控件(treeview和listview)绑定到同一个viewmodel。然而,我想知道如何将
TreeView
SelectedItem
绑定到viewmodel。正如我看到的,TreeView的
SelectedItem
是只读的。除非将viewmodel实现为一个
DependencyObject
,以支持一些
SelectedItem
bindable属性。但是那样的话,我们仍然需要一些代码。使用codebehind时,我们还可以处理
TreeView
SelectedItemChanged
事件,并相应地更新viewmodel的
SelectedItem
。@KingKing这种情况下的最佳解决方案是创建一个包装器对象,其中两个附加属性相互绑定。因此,我们将第一个道具绑定到SelectedItem,第二个道具绑定到viewmodel@Alexis看起来你是个专家。不确定。我在上面尝试的解决方案实际上可以正常工作,尽管它需要一些代码,因为TreeView的
SelectedItem
是只读的。如果可能的话,你应该发布这个问题的答案。甚至定义一个包装器也确实是在背后使用代码。事实上,我们无法避免代码落后,我们可以尽量避免使用它。@Alexis,您能不能添加一个示例,说明带有2个附加属性的包装器如何解决我的问题?也许我的“无代码落后”政策会走得更远,但每次我继续努力实现这一目标,我最终会得到真正干净的代码,所以我仍然充满希望。提前谢谢!