トップ回答者
バインドで参照元と編集反映先を別々にしたい

質問
-
いつもお世話になっております。
バインドしたデータ郡をListBoxに表示しています。
これを編集可能にしたく画面上部にテキストボックスを追加し、
それとListBoxのアイテムをバインドさせようと思いました。
ですがこのままだと、ListBoxにバインドする項目が2つになってしまうため違うような気がします。
このような場合、皆さんはどうされていますでしょうか?
ListBoxのアイテムを選択する度、バインドの切り替えみたいなことをするんでしょうか?
Xaml
<Window x:Class="List.ListView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="選択画面" Height="701" Width="700" ResizeMode="NoResize"> <StackPanel Height="660"> <TextBox x:Name="myTextBox" FontSize="20" Margin="271,18,182,0" Height="60" VerticalAlignment="Top" Text="{Binding ElementName=myLabel, Path = Content, Mode = TwoWay}" Width="191" /> <ListBox ItemsSource="{Binding Path = ListData}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" VerticalContentAlignment="Stretch" SelectedItem="{Binding SelectedData}" Margin="0,0,0,62" Height="676" Width="635"> <ListBox.ItemTemplate> <DataTemplate> <Border Background="LightBlue" Height= "150" Width="55" CornerRadius="10" Padding="10" Margin="3" > <WrapPanel> <!--! アイテム --> <Label x:Name="myLabel" Content="{Binding Path = Text}" FontSize="17" /> </WrapPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> </StackPanel> </Window>
ListBoxに表示するデータは以下のような感じでViewModel側のコンストラクタで生成してます
/// <summary> /// コンストラクタ時に表示データをセットする /// </summary> public ListViewModel() { this._ListData = new ObservableCollection<Hoge>(); this._ListData.Add(new Hoge() { Id = 1, Text = "太郎君" }); this._ListData.Add(new Hoge() { Id = 2, Text = "花子さん" }); this._ListData.Add(new Hoge() { Id = 3, Text = "ポチ" }); } private ObservableCollection<Hoge> _ListData; public ObservableCollection<Hoge> ListData { get { return this._ListData; } set { this._ListData = value; this.OnPropertyChanged("ListData"); } }
- 編集済み sumi_sumi 2012年12月26日 8:17
回答
-
わざわざListBoxItemから探さなくても、IsSynchronizedWithCurrentItemでカレントを同期させたり、
<TextBox x:Name="myTextBox" FontSize="20" Margin="271,18,182,0" Height="60" VerticalAlignment="Top" Text="{Binding Path = ListData/Text}" Width="191" /> <ListBox ItemsSource="{Binding Path = ListData}" IsSynchronizedWithCurrentItem="True" (略)
ListBoxのSelectedItemでとればいいじゃないかな。
<TextBox x:Name="myTextBox" FontSize="20" Margin="271,18,182,0" Height="60" VerticalAlignment="Top" Text="{Binding Path = SelectedItem.Text,ElementName=lst}" Width="191" /> <ListBox x:Name="lst" ItemsSource="{Binding Path = ListData}" IsSynchronizedWithCurrentItem="True"
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク sumi_sumi 2012年12月26日 23:57
-
> ListBoxのアイテムを選択する度、バインドの切り替えみたいなことをするんでしょうか?
私なら同じプロパティをバインドします。特別な意図がない限り、バインドを切り分けることはしていませんね。
以下VBですが、サンプルを考えてみました。ListBox.SelectedItem と TextBox.Text はともに SelectedData とバインドさせてます。(TextBox.Text は正確には SelectedData.Text とですが)
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="MainWindow" Height="250" Width="400"> <Window.DataContext> <local:ViewModel /> </Window.DataContext> <Grid> <StackPanel > <TextBox x:Name="myTextBox" FontSize="12" Margin="20,0,0,0" Height="40" Width="180" Text="{Binding SelectedData.Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderBrush="Blue"/> <ListBox Margin="0,0,0,62" ScrollViewer.HorizontalScrollBarVisibility="Disabled" VerticalContentAlignment="Stretch" ItemsSource="{Binding ListData}" SelectedItem="{Binding SelectedData}" > <ListBox.ItemTemplate> <DataTemplate> <Border Background="LightBlue" Height="50" Width="100" CornerRadius="10" Padding="10" Margin="3" > <WrapPanel> <Label x:Name="myLabel" Content="{Binding Text}" FontSize="17" /> </WrapPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> </StackPanel> </Grid> </Window>
Imports System.ComponentModel Imports System.Collections.ObjectModel Public Class ViewModel Implements INotifyPropertyChanged Public Sub New() Me._ListData = New ObservableCollection(Of Hoge)() Me._ListData.Add(New Hoge With {.Id = 1, .Text = "太郎君"}) Me._ListData.Add(New Hoge With {.Id = 2, .Text = "花子さん"}) Me._ListData.Add(New Hoge With {.Id = 3, .Text = "ポチ"}) End Sub Private _SelectedData As Hoge Public Property SelectedData() As Hoge <DebuggerNonUserCode()> Get Return _SelectedData End Get Set(value As Hoge) _SelectedData = value Me.RaisePropertyChanged("SelectedData") End Set End Property Private _ListData As ObservableCollection(Of Hoge) Public Property ListData() As ObservableCollection(Of Hoge) <DebuggerNonUserCode()> Get Return _ListData End Get Set(value As ObservableCollection(Of Hoge)) _ListData = value Me.RaisePropertyChanged("ListData") End Set End Property Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged Public Sub RaisePropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub End Class Public Class Hoge Public Property Id As Int32 Public Property Text As String End Class
ひらぽん http://d.hatena.ne.jp/hilapon/
- 編集済み ひらぽんModerator 2012年12月26日 8:51 画像追加
- 回答としてマーク sumi_sumi 2012年12月26日 23:57
すべての返信
-
わざわざListBoxItemから探さなくても、IsSynchronizedWithCurrentItemでカレントを同期させたり、
<TextBox x:Name="myTextBox" FontSize="20" Margin="271,18,182,0" Height="60" VerticalAlignment="Top" Text="{Binding Path = ListData/Text}" Width="191" /> <ListBox ItemsSource="{Binding Path = ListData}" IsSynchronizedWithCurrentItem="True" (略)
ListBoxのSelectedItemでとればいいじゃないかな。
<TextBox x:Name="myTextBox" FontSize="20" Margin="271,18,182,0" Height="60" VerticalAlignment="Top" Text="{Binding Path = SelectedItem.Text,ElementName=lst}" Width="191" /> <ListBox x:Name="lst" ItemsSource="{Binding Path = ListData}" IsSynchronizedWithCurrentItem="True"
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)
- 回答としてマーク sumi_sumi 2012年12月26日 23:57
-
> ListBoxのアイテムを選択する度、バインドの切り替えみたいなことをするんでしょうか?
私なら同じプロパティをバインドします。特別な意図がない限り、バインドを切り分けることはしていませんね。
以下VBですが、サンプルを考えてみました。ListBox.SelectedItem と TextBox.Text はともに SelectedData とバインドさせてます。(TextBox.Text は正確には SelectedData.Text とですが)
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="MainWindow" Height="250" Width="400"> <Window.DataContext> <local:ViewModel /> </Window.DataContext> <Grid> <StackPanel > <TextBox x:Name="myTextBox" FontSize="12" Margin="20,0,0,0" Height="40" Width="180" Text="{Binding SelectedData.Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderBrush="Blue"/> <ListBox Margin="0,0,0,62" ScrollViewer.HorizontalScrollBarVisibility="Disabled" VerticalContentAlignment="Stretch" ItemsSource="{Binding ListData}" SelectedItem="{Binding SelectedData}" > <ListBox.ItemTemplate> <DataTemplate> <Border Background="LightBlue" Height="50" Width="100" CornerRadius="10" Padding="10" Margin="3" > <WrapPanel> <Label x:Name="myLabel" Content="{Binding Text}" FontSize="17" /> </WrapPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> </StackPanel> </Grid> </Window>
Imports System.ComponentModel Imports System.Collections.ObjectModel Public Class ViewModel Implements INotifyPropertyChanged Public Sub New() Me._ListData = New ObservableCollection(Of Hoge)() Me._ListData.Add(New Hoge With {.Id = 1, .Text = "太郎君"}) Me._ListData.Add(New Hoge With {.Id = 2, .Text = "花子さん"}) Me._ListData.Add(New Hoge With {.Id = 3, .Text = "ポチ"}) End Sub Private _SelectedData As Hoge Public Property SelectedData() As Hoge <DebuggerNonUserCode()> Get Return _SelectedData End Get Set(value As Hoge) _SelectedData = value Me.RaisePropertyChanged("SelectedData") End Set End Property Private _ListData As ObservableCollection(Of Hoge) Public Property ListData() As ObservableCollection(Of Hoge) <DebuggerNonUserCode()> Get Return _ListData End Get Set(value As ObservableCollection(Of Hoge)) _ListData = value Me.RaisePropertyChanged("ListData") End Set End Property Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged Public Sub RaisePropertyChanged(ByVal propertyName As String) RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName)) End Sub End Class Public Class Hoge Public Property Id As Int32 Public Property Text As String End Class
ひらぽん http://d.hatena.ne.jp/hilapon/
- 編集済み ひらぽんModerator 2012年12月26日 8:51 画像追加
- 回答としてマーク sumi_sumi 2012年12月26日 23:57
-
gekkaさんの IsSynchronizedWithCurrentItem の回答、たいへん参考になりました。
IsSynchronizedWithCurrentItem の存在は恥ずかしながら私も知りませんでした。これを使えば SelectedItem を参照/保持せずうまく同期がとれますね。すごい便利です。勉強のため IsSynchronizedWithCurrentItem を使ったサンプルを C# で書いてみました。後からこのスレッド覗いた方が、何かの参考にして頂ければ幸いです。<(_ _)>
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication1" Title="MainWindow" Height="250" Width="400"> <Window.DataContext> <local:ViewModel /> </Window.DataContext> <Grid> <StackPanel > <TextBox FontSize="12" Margin="20,0,0,0" Height="40" Width="180" Text="{Binding ListData/Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" BorderBrush="Blue"/> <ListBox Margin="0,0,0,62" ScrollViewer.HorizontalScrollBarVisibility="Disabled" VerticalContentAlignment="Stretch" ItemsSource="{Binding ListData}" IsSynchronizedWithCurrentItem="True" > <ListBox.ItemTemplate> <DataTemplate> <Border Background="LightBlue" Height="50" Width="100" CornerRadius="10" Padding="10" Margin="3" > <WrapPanel> <Label Content="{Binding Text}" FontSize="17" /> </WrapPanel> </Border> </DataTemplate> </ListBox.ItemTemplate> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel/> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> </StackPanel> </Grid> </Window>
using System.Collections.ObjectModel; using System.ComponentModel; namespace WpfApplication1 { class ViewModel : INotifyPropertyChanged { public ViewModel() { this.ListData = new ObservableCollection<Hoge>(); this.ListData.Add(new Hoge { Id = 1, Text = "太郎君" }); this.ListData.Add(new Hoge { Id = 2, Text = "花子さん" }); this.ListData.Add(new Hoge { Id = 3, Text = "ポチ" }); this.ListData.Add(new Hoge { Id = 3, Text = "鈴木" }); } ObservableCollection<Hoge> _ListData; public ObservableCollection<Hoge> ListData { get { return _ListData; } set { if (_ListData != value) { _ListData = value; this.RaisePropertyChanged("ListData"); } } } public event PropertyChangedEventHandler PropertyChanged; public void RaisePropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } } class Hoge { public int Id { get; set; } public string Text { get; set; } } }
ひらぽん http://d.hatena.ne.jp/hilapon/
- 編集済み ひらぽんModerator 2012年12月28日 1:12