none
バインドで参照元と編集反映先を別々にしたい RRS feed

  • 質問

  • いつもお世話になっております。

    バインドしたデータ郡を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
    2012年12月26日 8:13

回答

  • わざわざ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
    2012年12月26日 8:44
  • > 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/


    2012年12月26日 8:47
    モデレータ

すべての返信

  • わざわざ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
    2012年12月26日 8:44
  • > 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/


    2012年12月26日 8:47
    モデレータ
  • 素朴な疑問ですが、ListBoxを編集可能にするのであれば、ItemTemplate内をLabelではなくTextBoxにすれば良いのではないでしょうか?
    ちなみにWPFのLabelはWindowsフォームのLabelとは目的が違います。表示するだけであればTextBlockがより適しているでしょう。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/

    2012年12月26日 14:32
    モデレータ
  • gekkaさま

    有難う御座います。

    IsSynchronizedWithCurrentItemというもの初めて知りました。

    なるほど、わざわざListBoxではなく、ソース元のデータ(ListData)とバインドさせるわけですね。

    おかげで解決しました。有難う御座います。

    2012年12月26日 23:52
  • ひらぽんさま

    有難う御座います。

    そうですよね、切り替えるなんてスマートじゃないですよね。よほどの意図がない限り。。。

    UpdateSourceTriggerというもの初めて知りました。

    この方法も大変参考になります。有難う御座います。

    2012年12月26日 23:54
  • trapemiyaさま

    有難う御座います。

    >ItemTemplate内をLabelではなくTextBoxにすれば良いのではないでしょうか?

    仰る通りだと思います。今回は他の画面との共通性のためこのような仕様に変更になったのでした・・・・。

    >ちなみにWPFのLabelはWindowsフォームのLabelとは目的が違います。表示するだけであればTextBlockがより適しているでしょう。有難う御座います。大丈夫です。

    実際はLabelではなく、自作のUserControlで表示しております。

    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/


    2012年12月27日 3:46
    モデレータ