2017-03-01 165 views
2

我想在WPF中创建一个包含内容集合的用户控件,并努力让绑定在子元素上正常工作。我已经看了这个例子,但我一个层面更深入地嵌套,这和因此该方案并没有帮助:Data Binding in WPF User Controls如何处理包含列表的WPF用户控件中的嵌套绑定?

这里是我的用户:

<UserControl x:Class="BindingTest.MyList" 
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
      mc:Ignorable="d" 
      d:DesignHeight="300" d:DesignWidth="300" 
      x:Name="uc"> 
    <Grid> 
     <ListView Grid.Row="1" ItemsSource="{Binding Items, ElementName=uc}"> 
      <ListView.ItemTemplate> 
       <DataTemplate> 
        <Label Content="{Binding Text}" /> 
       </DataTemplate> 
      </ListView.ItemTemplate> 
     </ListView> 
    </Grid> 
</UserControl> 

而隐藏代码:

[ContentProperty("Items")] 
public partial class MyList : UserControl { 
    public MyList() { 
     InitializeComponent(); 
    } 

    public ObservableCollection<MyItem> Items { get; set; } = new ObservableCollection<MyItem>(); 
} 

public class MyItem : DependencyObject { 
    public string Text { 
     get { 
      return (string)this.GetValue(TextProperty); 
     } 
     set { 
      this.SetValue(TextProperty, value); 
     } 
    } 
    public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyItem), new PropertyMetadata()); 
} 

然后我如何在应用程序中使用它:

<Window x:Class="BindingTest.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:local="clr-namespace:BindingTest" 
     Title="MainWindow" Height="350" Width="525" 
     DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="Auto" /> 
      <RowDefinition Height="*" /> 
      <RowDefinition Height="*" /> 
     </Grid.RowDefinitions> 

     <TextBox Grid.Row="0" Text="{Binding Val1, Mode=TwoWay}" /> 

     <local:MyList Grid.Row="1"> 
      <local:MyItem Text="{Binding Val1}" /> 
      <local:MyItem Text="Two" /> 
      <local:MyItem Text="Three" /> 
     </local:MyList> 

     <ListView Grid.Row="2"> 
      <ListViewItem Content="{Binding Val1}" /> 
      <ListViewItem Content="Bravo" /> 
      <ListViewItem Content="Charlie" /> 
     </ListView> 
    </Grid> 
</Window> 

和FI应受我的窗口:

public partial class MainWindow : Window, INotifyPropertyChanged { 
    public MainWindow() { 
     InitializeComponent(); 
    } 

    string _val1 = "Foo"; 
    public string Val1 { 
     get { return _val1; } 
     set { 
      _val1 = value; 
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Val1")); 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged; 
} 

我现在遇到的问题是,第一个“MyItem”在用户控制最终显示的是空白的文本,而在库存控制显示美孚第一个ListViewItem如你所愿。

我明白这是因为在MyList控件中,DataTemplate为集合中的每个项目切换DataContext,但无论我在绑定表达式中尝试什么,无论是在窗口还是用户控件中,我都无法获取它正确绑定到窗口上的Val1。

我在做什么错?

回答

0

其实,我找到了一种方法来使这个工作正确。我不用制作UserControl,而是切换到ItemsControl,并将“MyItem”类更改为子类ContentControl。然后WPF负责为您设置数据上下文。您可以为ItemsControl指定一个新模板,以避免网格在列表中显示为自己的项目。

<ItemsControl x:Class="BindingTest.MyList" 
       xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
       xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
       xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
       mc:Ignorable="d" 
       d:DesignHeight="300" d:DesignWidth="300" 
       x:Name="uc"> 
    <ItemsControl.Template> 
     <ControlTemplate> 
      <Grid> 
       <ListView ItemsSource="{Binding Items, ElementName=uc}"> 
        <ListView.Resources> 
         <Style TargetType="ListViewItem"> 
          <Setter Property="Template"> 
           <Setter.Value> 
            <ControlTemplate TargetType="ListViewItem"> 
             <Label Foreground="Blue" Content="{Binding Path=Text}" /> 
            </ControlTemplate> 
           </Setter.Value> 
          </Setter> 
         </Style> 
        </ListView.Resources> 
       </ListView> 
      </Grid> 
     </ControlTemplate> 
    </ItemsControl.Template> 
</ItemsControl> 

和:

public partial class MyList : ItemsControl { 
    public MyList() { 
     InitializeComponent(); 
    } 
} 

public class MyItem : ContentControl { 
    public string Text { 
     get { 
      return (string)this.GetValue(TextProperty); 
     } 
     set { 
      this.SetValue(TextProperty, value); 
     } 
    } 
    public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(MyItem), new PropertyMetadata()); 
} 
0

您的MyItem对象的DataContext没有要绑定的Val1值,这就是绑定破坏的原因。这与DataGrids中常见的问题相同,即内部项目(MyItem)的上下文被设置为ItemSource的元素(在您的情况下为ObservableCollection Items)。

他们的方式我一直都有解决这个问题的方法是在DataGrid或Control的资源中包含一个对控件的DataContext的引用。通过这种方式,您可以将该对象绑定为StaticResource,并且不必更改控件中的XAML。

类,用于保存数据方面:

public class BindingProxy : Freezable 
{ 
    protected override Freezable CreateInstanceCore() { return new BindingProxy(); } 

    public static readonly DependencyProperty ContextProperty = 
     DependencyProperty.Register("Context", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null)); 

    public object Context 
    { 
     get { return (object)GetValue(ContextProperty); } 
     set { SetValue(ContextProperty, value); } 
    } 
} 

然后BindingProxy的一个实例添加到您的MYLIST的资源,并结合上下文属性将MYLIST的结合。然后,您可以通过名称将其作为StaticResource绑定到BindingProxy。该路径指向资源的上下文属性,然后是要绑定到的值的常规路径。

<local:MyList Grid.Row="1"> 
     <local:MyList.Resources> 
      <local:BindingProxy x:Key="VMProxy" Context="{Binding}"/> 
     </local:MyList.Resources> 
     <local:MyItem Text="{Binding Path=Context.Val1, Source={StaticResource VMProxy}}"/> 
     <local:MyItem Text="Two" /> 
     <local:MyItem Text="Three" /> 
    </local:MyList> 

我测试了它的控制和窗口代码,你有它,它的工作就像一个魅力。 我不能完全相信这个解决方案,因为我在这里找到它,但对于我的生活,我无法找到有DataGrids此解决方案的旧答案。