none
ObservableCollectionをBindingする方法を教えてください。 RRS feed

  • 質問

  • TypeIndexの値によって表示する内容を変えるコードを書きました。

    下記のコードを実行するをItemControls[0]のTypeIndexが1のときはUserControl1が3つ出てきてしまい、UserControl2は表示されません。

    期待する実行結果は、UserControl2、UserControl1、UserControl2の順に表示されることです。

    xamlの書き方が悪いと思いますので正しい書き方をご教示いただけないでしょうか。

    public class MainWindowViewModel
        {
            public ObservableCollection<ItemControl> ItemControls { get; private set; }
    
            public MainWindowViewModel()
            {
                this.ItemControls = new ObservableCollection<ItemControl>()
                {
                    new ItemControl(){TypeIndex = "2"},
                    new ItemControl(){TypeIndex = "1"},
                    new ItemControl(){TypeIndex = "2"},
                };
            }
        }
    
        public class ItemControl
        {
            public string TypeIndex { get; set; }
        }

    <Window.Resources>
            <ResourceDictionary>
                <DataTemplate x:Key="MyTemplate1" DataType="{x:Type local:UserControl1ViewModel}">
                    <local:UserControl1 />
                </DataTemplate>
                <DataTemplate x:Key="MyTemplate2" DataType="{x:Type local:UserControl2ViewModel}">
                    <local:UserControl2 />
                </DataTemplate>
    
                <Style x:Key="SelectableContentStyle" TargetType="{x:Type ItemsControl}">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Path=ItemControls/TypeIndex}" Value="1">
                            <Setter Property="ItemTemplate" Value="{StaticResource MyTemplate1}"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding Path=ItemControls/TypeIndex}" Value="2">
                            <Setter Property="ItemTemplate" Value="{StaticResource MyTemplate2}"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ResourceDictionary>
        </Window.Resources>
        <Grid>
            <StackPanel>
                <TextBlock Text="Multi"/>
                <ItemsControl ItemsSource="{Binding Path=ItemControls}" 
                              Style="{StaticResource SelectableContentStyle}">
                    <ItemsControl.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapPanel Orientation="Vertical"/>
                        </ItemsPanelTemplate>
                    </ItemsControl.ItemsPanel>
                </ItemsControl>
            </StackPanel>
        </Grid>

    2019年6月13日 4:46

回答

  • 一番直感的なのは、トリガでやるのではなく、ItemTemplateSelectorを使って適用するDataTemplateを差し替えることかと思います。

    トリガでやるのであれば、ItemsControl.ItemContainerStyleにContentPresenter向けのStyleを設定して、そこで <DataTrigger Binding="{Binding TypeIndex}" Value="..."><Setter Property="ContentTemplate" Value="..."/>みたいなトリガを書くことになるでしょうか。

    • 回答としてマーク yayamama18 2019年6月13日 6:44
    2019年6月13日 5:34
  • TypeIndexというプロパティで分けるのではなく、VMの型自体を分けることができるのならば、DataTemplate::DataTypeを使うことで一々切り替えるコードを書く必要もなくなります。

    abstract class VMControlBase : INotifyPropertyChanged {
    }
    class VMControl1 : VMControlBase {
    }
    class VMControl2 : VMControlBase {
    }
    
    class MainVM {
      public ObservableCollection<VMControlBase> Controls { get; }
    }
    <ItemsControl ItemsSource="{Binding Controls}">
      <!-- DataTemplateはもっと上のResourcesで定義してもいい -->
      <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type local:VMControl1}">
          <TextBlock Text="{Binding ...}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:VMControl2}">
          <TextBlock Text="{Binding ...}"/>
        </DataTemplate>
      </ItemsControl.Resources>
    </ItemsControl>

    • 編集済み Hongliang 2019年6月13日 5:46
    • 回答としてマーク yayamama18 2019年6月13日 6:42
    • 回答としてマークされていない yayamama18 2019年6月13日 6:42
    • 回答としてマーク yayamama18 2019年6月13日 6:44
    2019年6月13日 5:45

すべての返信

  • 一番直感的なのは、トリガでやるのではなく、ItemTemplateSelectorを使って適用するDataTemplateを差し替えることかと思います。

    トリガでやるのであれば、ItemsControl.ItemContainerStyleにContentPresenter向けのStyleを設定して、そこで <DataTrigger Binding="{Binding TypeIndex}" Value="..."><Setter Property="ContentTemplate" Value="..."/>みたいなトリガを書くことになるでしょうか。

    • 回答としてマーク yayamama18 2019年6月13日 6:44
    2019年6月13日 5:34
  • TypeIndexというプロパティで分けるのではなく、VMの型自体を分けることができるのならば、DataTemplate::DataTypeを使うことで一々切り替えるコードを書く必要もなくなります。

    abstract class VMControlBase : INotifyPropertyChanged {
    }
    class VMControl1 : VMControlBase {
    }
    class VMControl2 : VMControlBase {
    }
    
    class MainVM {
      public ObservableCollection<VMControlBase> Controls { get; }
    }
    <ItemsControl ItemsSource="{Binding Controls}">
      <!-- DataTemplateはもっと上のResourcesで定義してもいい -->
      <ItemsControl.Resources>
        <DataTemplate DataType="{x:Type local:VMControl1}">
          <TextBlock Text="{Binding ...}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:VMControl2}">
          <TextBlock Text="{Binding ...}"/>
        </DataTemplate>
      </ItemsControl.Resources>
    </ItemsControl>

    • 編集済み Hongliang 2019年6月13日 5:46
    • 回答としてマーク yayamama18 2019年6月13日 6:42
    • 回答としてマークされていない yayamama18 2019年6月13日 6:42
    • 回答としてマーク yayamama18 2019年6月13日 6:44
    2019年6月13日 5:45