質問
2012年2月9日木曜日 16:30
お世話になります。
VisualStudio2010 ; WPF(C#) ; MVVM にて、
(;なるべくコードビハインドを使用しない方針で作成しています)
ファイル内容を画面に表示するアプリケーションを作成しています。
画面内にDataGridが1つとラジオボタンが2つあります。
ラジオボタンで表示するファイル内容を切り替える仕様です。
ラジオボタンで表示するファイルを選択して、
DataGridに表示させたいと思い、以下の記述をしました。
<DataGrid
ItemsSource="{Binding Path=HOGE}"/>
HOGEはViewModel内に宣言されたプロパティで、ファイル内容を;納した、
ObservableCollection<classA>を正しく表示しています。
;これを、例えば、別のラジオボタンチェックされ、表示ボタンが押下された時、
ObservableCollection<classB>に、バインド先を変更したいのですが、
どのようにすればよいでしょうか(可能なのでしょうか)。
ご存じの方がおられましたら教えてください。よろしくお願いします。
すべての返信 (7)
2012年2月9日木曜日 22:02 ✅回答済み
VMで、CollectionViewSourceを持たせて、それのViewプロパティをVMのプロパティとして公開して、DataGridとバインド。
表示データの切り替えは、VMでCollectionViewSourceのSourceプロパティを差し替えで実現出来ないでしょうか?
かずき Blog:http://d.hatena.ne.jp/okazuki/ VS 2010のデザイナでBlendのBehaviorをサポートするツール公開してます。 http://vsbehaviorsupport.codeplex.com/
2012年2月10日金曜日 0:40 ✅回答済み
ObservableCollection<classB>に、バインド先を変更したいのですが、
どのようにすればよいでしょうか(可能なのでしょうか)。
どこまで求められているのかわからないのですが、バインド先を変える必要はなく、ViewModelで単にHOGEプロパティにObservableCollection<classB>を設定すれば良いだけのような気がします。
#(追記)HOGEをobject型で公開して下さい。DataGridのAutoGenerateColumnsはTrueであることを前提としています。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
2012年2月10日金曜日 2:15 ✅回答済み
VMで、CollectionViewSourceを持たせて、それのViewプロパティをVMのプロパティとして公開して、DataGridとバインド。
表示データの切り替えは、VMでCollectionViewSourceのSourceプロパティを差し替えで実現出来ないでしょうか?
CollectionViewSource、面白そうですね。 後学のため、上記かずきさんの回答を参考にして少し考えてみました。何かの参考になれば幸いです。あと MVVM インフラは Livet を使ってます。CollectionViewSource に関しては以下の記事を参考にしました。
http://msdn.microsoft.com/ja-jp/library/ms752347.aspx
まずModel
using Livet;
public class ModelA : NotificationObject {
public int Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
}
public class ModelB : NotificationObject {
public int Id { get; set; }
public string Name { get; set; }
public string Telephone { get; set; }
}
次に ViewModelです。
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Livet;
public class MainWindowViewModel : ViewModel {
public MainWindowViewModel() {
this.SourceA = new ObservableCollection<modela>();
this.SourceA.Add(new ModelA() { Id = 0, Name = "田中", Address = "東京都" });
this.SourceA.Add(new ModelA() { Id = 1, Name = "佐藤", Address = "埼玉県" });
this.SourceB = new ObservableCollection<modelb>();
this.SourceB.Add(new ModelB() { Id = 1234, Name = "日本商事株式会社", Telephone = "06xxxxxxxx" });
this.SourceB.Add(new ModelB() { Id = 5678, Name = "有限会社佐伯物産", Telephone = "03xxxxxxxx" });
}
#region SourceA変更通知プロパティ
private ObservableCollection<modela> _SourceA;
public ObservableCollection<modela> SourceA {
get { return _SourceA; }
set {
if (EqualityComparer<observablecollection<modela>>.Default.Equals(_SourceA, value))
return;
_SourceA = value;
RaisePropertyChanged("SourceA");
}
}
#endregion
#region SourceB変更通知プロパティ
private ObservableCollection<modelb> _SourceB;
public ObservableCollection<modelb> SourceB {
get { return _SourceB; }
set {
if (EqualityComparer<observablecollection<modelb>>.Default.Equals(_SourceB, value))
return;
_SourceB = value;
RaisePropertyChanged("SourceB");
}
}
#endregion
#region IsA変更通知プロパティ
private bool _IsA;
public bool IsA {
get { return _IsA; }
set {
if (EqualityComparer<bool>.Default.Equals(_IsA, value))
return;
_IsA = value;
RaisePropertyChanged("IsA");
}
}
#endregion
#region IsB変更通知プロパティ
private string _IsB;
public string IsB {
get { return _IsB; }
set {
if (EqualityComparer<string>.Default.Equals(_IsB, value))
return;
_IsB = value;
RaisePropertyChanged("IsB");
}
}
#endregion
}</string></bool></observablecollection<modelb></modelb></modelb></observablecollection<modela></modela></modela></modelb></modela>
最後に View です。
<window Height="350" Width="525" title="MainWindow" x:Class="LivetWPFApplication1.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:l="http://schemas.livet-mvvm.net/2011/wpf" xmlns:v="clr-namespace:LivetWPFApplication1.Views" xmlns:vm="clr-namespace:LivetWPFApplication1.ViewModels" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<window.datacontext>
<vm:mainwindowviewmodel></vm:mainwindowviewmodel>
</window.datacontext>
<window.resources>
<collectionviewsource Source="{Binding SourceA}" x:Key="SourceA">
<collectionviewsource Source="{Binding SourceB}" x:Key="SourceB">
</collectionviewsource></collectionviewsource></window.resources>
<grid>
<radiobutton Content="IsA" Height="26" HorizontalAlignment="Left" IsChecked="{Binding IsA}" Margin="12,23,0,0" Name="radioButton1" VerticalAlignment="Top" Width="60"></radiobutton>
<radiobutton Content="IsB" Height="26" HorizontalAlignment="Left" IsChecked="{Binding IsB}" Margin="78,23,0,0" Name="radioButton2" VerticalAlignment="Top" Width="60"></radiobutton>
<datagrid AutoGenerateColumns="True" Height="236" HorizontalAlignment="Left" Margin="12,63,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="479">
<datagrid.style>
<style TargetType="DataGrid">
<Style.Triggers>
<DataTrigger Binding="{Binding IsA}" Value="True" >
<Setter Property="ItemsSource" Value="{Binding Source={StaticResource SourceA}}" ></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsB}" Value="True" >
<Setter Property="ItemsSource" Value="{Binding Source={StaticResource SourceB}}" ></Setter>
</DataTrigger>
</Style.Triggers>
</style>
</datagrid.style>
</datagrid>
</grid>
</window>
するとこうなる。
もっとスマートな方法がありそうですが、あくまで参考まで。あしからず。
ひらぽん http://d.hatena.ne.jp/hilapon/
2012年2月10日金曜日 5:03 ✅回答済み
後から考えたら、CollectionViewSource 使わなくても DataTrigger で ItemsSource 切り替えるだけですみますね。以下、変更した XAML です。
<Window x:Class="LivetWPFApplication2.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
xmlns:v="clr-namespace:LivetWPFApplication2.Views"
xmlns:vm="clr-namespace:LivetWPFApplication2.ViewModels"
Title="MainWindow" Height="222" Width="525">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<RadioButton Content="IsA" Height="26" HorizontalAlignment="Left"
Margin="12,23,0,0" Name="radioButton1" VerticalAlignment="Top" Width="60"
IsChecked="{Binding IsA}" />
<RadioButton Content="IsB" Height="26" HorizontalAlignment="Left"
Margin="78,23,0,0" Name="radioButton2" VerticalAlignment="Top" Width="60"
IsChecked="{Binding IsB}" />
<DataGrid AutoGenerateColumns="True" Height="106" HorizontalAlignment="Left"
Margin="12,63,0,0" Name="dataGrid1" VerticalAlignment="Top" Width="479" >
<DataGrid.Style>
<Style TargetType="DataGrid" >
<Style.Triggers>
<DataTrigger Binding="{Binding IsA}" Value="True" >
<Setter Property="ItemsSource" Value="{Binding SourceA}" />
</DataTrigger>
<DataTrigger Binding="{Binding IsB}" Value="True" >
<Setter Property="ItemsSource" Value="{Binding SourceB}" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
</DataGrid>
</Grid>
</Window>
でも trapemiya さんの方法がよりシンプルですね(汗
ひらぽん http://d.hatena.ne.jp/hilapon/
2012年2月10日金曜日 5:15 ✅回答済み
trapemiya さんの回答案だとこうなる・・・確かにシンプルですね。
<DataGrid AutoGenerateColumns="True" ItemsSource="{Binding Hoge}" />
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Livet;
public class MainWindowViewModel : ViewModel {
private ObservableCollection<ModelA> _SourceA;
private ObservableCollection<ModelB> _SourceB;
public MainWindowViewModel() {
_SourceA = new ObservableCollection<ModelA>();
_SourceA.Add(new ModelA() { Id = 0, Name = "田中", Address = "東京都" });
_SourceA.Add(new ModelA() { Id = 1, Name = "佐藤", Address = "埼玉県" });
_SourceB = new ObservableCollection<ModelB>();
_SourceB.Add(new ModelB() { Id = 1234, Name = "日本商事株式会社", Telephone = "06xxxxxxxx" });
_SourceB.Add(new ModelB() { Id = 5678, Name = "有限会社佐伯物産", Telephone = "03xxxxxxxx" });
this.IsA = true;
}
#region Hoge変更通知プロパティ
private object _Hoge;
public object Hoge {
get { return _Hoge; }
set {
if (EqualityComparer<object>.Default.Equals(_Hoge, value))
return;
_Hoge = value;
RaisePropertyChanged("Hoge");
}
}
#endregion
#region IsA変更通知プロパティ
private bool _IsA;
public bool IsA {
get { return _IsA; }
set {
if (EqualityComparer<bool>.Default.Equals(_IsA, value))
return;
_IsA = value;
RaisePropertyChanged("IsA");
if (_IsA) {
this.Hoge = _SourceA;
}
}
}
#endregion
#region IsB変更通知プロパティ
private bool _IsB;
public bool IsB {
get { return _IsB; }
set {
if (EqualityComparer<bool>.Default.Equals(_IsB, value))
return;
_IsB = value;
RaisePropertyChanged("IsB");
if (_IsB) {
this.Hoge = _SourceB;
}
}
}
#endregion
}
ひらぽん http://d.hatena.ne.jp/hilapon/
2012年2月12日日曜日 3:51 ✅回答済み
私のイメージだとこんな感じですね~。ひらぽんさんがLivetで書かれてるので、私もLivetで。
まず、ViewModel側。
namespace DataGridSample.ViewModels
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Data;
using Livet;
using Livet.Commands;
public class MainWindowViewModel : ViewModel
{
#region ChangeDataCommand
private ViewModelCommand _ChangeDataCommand;
/// <summary>
/// 表示データの切り替えを行うコマンド
/// </summary>
public ViewModelCommand ChangeDataCommand
{
get
{
if (_ChangeDataCommand == null)
{
_ChangeDataCommand = new ViewModelCommand(ChangeData);
}
return _ChangeDataCommand;
}
}
public void ChangeData()
{
// CheckBoxの状態に応じてCollectionViewSourceのSourceを変える
if (this.IsA)
{
this.DataGridDataSource.Source = this.modelAItems;
}
else if (this.IsB)
{
this.DataGridDataSource.Source = this.modelBItems;
}
}
#endregion
#region A変更通知プロパティ
private bool _IsA = true;
public bool IsA
{
get
{ return _IsA; }
set
{
if (EqualityComparer<bool>.Default.Equals(_IsA, value))
return;
_IsA = value;
RaisePropertyChanged("IsA");
}
}
#endregion
#region IsB変更通知プロパティ
private bool _IsB;
public bool IsB
{
get
{ return _IsB; }
set
{
if (EqualityComparer<bool>.Default.Equals(_IsB, value))
return;
_IsB = value;
RaisePropertyChanged("IsB");
}
}
#endregion
// 表示用のでーた
private ObservableCollection<ModelA> modelAItems = new ObservableCollection<ModelA>(ModelA.Load());
private ObservableCollection<ModelB> modelBItems = new ObservableCollection<ModelB>(ModelB.Load());
// DataGridにバインドするためのCollectionViewSource
public CollectionViewSource DataGridDataSource { get; private set; }
public MainWindowViewModel()
{
// 初期状態はModelAを表示するようにする
this.DataGridDataSource = new CollectionViewSource { Source = modelAItems };
}
}
// 表示用のダミーデータ
public class ModelA
{
public string ModelAProperty { get; set; }
public static IEnumerable<ModelA> Load()
{
return Enumerable.Range(1, 10).Select(i => new ModelA { ModelAProperty = "ModelA: " ; i });
}
}
public class ModelB
{
public string ModelBProperty { get; set; }
public static IEnumerable<ModelB> Load()
{
return Enumerable.Range(1, 10).Select(i => new ModelB { ModelBProperty = "ModelB: " ; i });
}
}
}
そして、XAML側。DataGridのItemsSourceのBindingでUpdateSourceTriggerにPropertyChangedつけてないと表示が切り替わらないので注意って感じです。
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
xmlns:l="http://schemas.livet-mvvm.net/2011/wpf"
xmlns:v="clr-namespace:DataGridSample.Views"
xmlns:vm="clr-namespace:DataGridSample.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="DataGridSample.Views.MainWindow"
Title="MainWindow" Height="350" Width="525" d:DataContext="{d:DesignData /SampleData/MainWindowViewModelSampleData.xaml}">
<Window.DataContext>
<vm:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel x:Name="stackPanel1" Orientation="Horizontal" HorizontalAlignment="Right">
<RadioButton Content="A" x:Name="radioButton1" Margin="2.5" Padding="0" IsChecked="{Binding IsA}" />
<RadioButton Content="B" x:Name="radioButton2" Margin="2.5" Padding="0" IsChecked="{Binding IsB}" />
<Button Content="ChangeData" Margin="0,0,0,8" MinWidth="75" Command="{Binding ChangeDataCommand}"/>
</StackPanel>
<DataGrid Grid.Row="1" x:Name="dataGrid1" ItemsSource="{Binding Mode=OneWay, Path=DataGridDataSource.View, UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</Window>
かずき Blog:http://d.hatena.ne.jp/okazuki/ VS 2010のデザイナでBlendのBehaviorをサポートするツール公開してます。 http://vsbehaviorsupport.codeplex.com/
2012年2月13日月曜日 1:37
ふむ・・・さすが、かずき_okazukiさん、素晴らしいサンプルありがとうございます。
Enumerable.Range の使い方、たいへん参考になります。(今までどう使うか知らなかった(汗))
ちなみにここで CollectionViewSource を使っておられますが、やはり DataGrid とのバインドには CollectionViewSource を使った方がいいとの見解ですか?