2017-03-01 9 views
2

コンテンツコレクションを含むWPFでusercontrolを作成しようとしていて、バインディングをサブ要素で正しく機能させるために苦労しています。私はすでに、この例を見て、私は1つのレベルは、より深く、このので、この解決策で問題が解決しないことを入れ子にしています: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は」あなたが期待するよう在庫管理・ディスプレイのFooの最初の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値を持たないため、バインディングが破損しているのは間違いありません。これは、内部アイテム(MyItem)のコンテキストがItemSourceの要素(あなたの場合はObservableCollection Items)に設定されているDataGridsでよく見られるのと同じ問題です。

私はいつも問題を回避していますが、DataGridまたはコントロールの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の結合にコンテキストプロパティをバインドします。次に、名前でStaticResourceとしてBindingProxyにバインドできます。 PathはリソースのContextプロパティへのパスで、次にバインドする値への通常パスです。

<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のこの解決策を持つ古い答えを見つけることができません。

関連する問題