如何在WPF中执行多项数据绑定

时间:2014-03-04 08:55:04

标签: c# wpf xaml mvvm data-binding

我有一个特定的场景。我的申请看起来像这样。 enter image description here

在左侧有一些用户列表,它是ListBox,在右侧有几个字段,它们是绑定到左侧的数据。它的工作原理是,如果您在右侧选择“用户1”,则会出现相关信息,您可以修改该信息,并且它是与"UpdateSourceTrigger=PropertyChanged"绑定的数据,因此它也会立即反映在左侧。其他用户也是如此。

现在的问题是,如果我选择多个用户并编辑一个字段,请说字段3是可编辑的文本框。现在,如果我选择用户1并编辑此文本框,它会反映在用户1“注意:...”中,如果我选择用户2并编辑字段3,则会更新用户2“注意:...”但是如果是多选我如何实现它?假设我想选择用户1和用户2两者并编辑注释字段它应该更新用户1和用户2的注​​释字段,数据绑定也应该工作我的意思是它应该立即我要进入文本框的文本。任何想法我怎样才能做到这一点?

目前在我的viewModel

模型

public String Note
        {
            get
            {
                return (String)GetValue(NoteProperty);
            }
            set { SetValue(NoteProperty, value); }
        }

查看

在XAML中,User ListBox Items模板定义如下

<TextBlock Text="{Binding Note, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

并且在XAML中,右侧文本框(字段3)是以相同方式绑定的数据

<TextBox Text="{Binding Note, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"  />

如何实现多用户数据绑定?

请帮助并给我一些想法。

编辑:

转换器:

public class MultiBindingConverter : IValueConverter
{
    ObservableCollection<Info> mycollection;

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var coll = (ObservableCollection<Info>)value;
        mycollection = coll;
        if (coll.Count == 1)
        {
            if (parameter.ToString() == "FNote")
                return coll[0];
        }
        else if (coll.Count > 1)
        {
            // string name = coll[0].FirstName;
            if (parameter.ToString() == "FNote")
            {
                string name = coll[0].Note;
                foreach (var c in coll)
                {
                    if (c.Note != name)
                        return null;
                    else continue;
                }
                return name;
            }
        }
        return null;
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {

        if (parameter.ToString() == "FNote")
        {
            foreach (var c in mycollection)
            {
                c.Note = value.ToString();
            }
            return mycollection;
        }
        return null;
    }
}

对我来说,只有一个TextBox Editable NoteTextBox需要与多个用户进行DataBinded。

在我的ViewModel

我写过

视图模型

private Command selectionChangedCommand;
        public Command SelectionChangedCommand
        {
            get
            {
                if (selectionChangedCommand == null)
                {
                    selectionChangedCommand = new Command(SelectionChanged, true);
                }
                return selectionChangedCommand;
            }
            set { selectionChangedCommand = value; }
        }
        public void SelectionChanged(object value)
        {
            selectedItem =  new ObservableCollection<Info>((value as IEnumerable).OfType<Info>());

        }
        private ObservableCollection<Info> selectedItem;

        public ObservableCollection<Info> SelectedItem
        {
            get { return selectedItem; }
            set
            {
                selectedItem = value;
                PropertyChanged("SelectedItem");
            }
        }

Info课程中,有一个属性Note需要绑定到View的两个位置。

3 个答案:

答案 0 :(得分:2)

我完全同意@GazTheDestroyer ...这种数据绑定不能仅通过数据绑定来实现。 @Kumar所建议的是作为POC工作,但是当你在一个实时项目中并且使用模型,viewModel和视图以及许多具有一个视图模型的UserControl或一个具有两个ViewModel的用户控件时,则难以实现此场景无法猜测。

好的,没有理论了。我已经实现了这一点,我将分享我是如何做到的。

一对一的DataBinding非常完美且工作正常。当您选择用户4时,此用户Note字段和Field3 Editable NoteBox绑定到同一属性,因此它可以正常工作。

在多个选择中首先选择User4,然后选择User3user1,当选择多个项目时,我在代码中加入逻辑Note文字是空的。这不是反对的 MVVM根据某些视图标准更新视图并不会破坏MVVM模式。所以现在当更新可编辑文本框时,在viewModel中更新了一些文本user4属性。现在困难的部分是更新其他选定的用户。以下是将更新所选用户的代码,并将反映我提到的Mode="TwoWay", UpdateSourceTriger="PropertyChanged"

if (listUser.SelectedItems.Count > 1)
{
  for (int i = 0; i < listUser.SelectedItems.Count; i++)
    {
     Info info = listUser.SelectedItems[i] as Info;
     info.Note = (string)tbNote.Text;
    }
}

通过这种方式,Editable note textbox的值在所有用户Note Property的属性中更新,并且绑定是双向的,它也会反映在其他用户中。

可能有很多方法可以解决它,但我找到了这种方式并且它工作得非常好,所以我想我会回答我自己的问题。

答案 1 :(得分:1)

单独通过数据绑定无法实现这一点,因为在某些情况下您需要做出逻辑决策。

例如,如果user1和user2具有不同的notetext,那么当两者都被选中时,您无法同时显示两者。相反,我想你想要一些方法来指定你想要“保留原始文本”,或者允许用户过度输入以将两个文本设置为相同。

无论您想要什么,都需要在viewmodel中拥有单独的绑定源,以便您可以独立更新它们并做出合理的决策。

答案 2 :(得分:1)

我尝试了一些我知道的东西,我的输出就像你的要求。如果我错了,请纠正我。

XAML

<Window x:Class="MVVM_sample_ListBox.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:MVVM_sample_ListBox"
            Title="MainWindow" Height="350" Width="525"
            xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity">
        <Window.Resources>
            <local:Converter x:Key="Converter"/>
        </Window.Resources>    
       <Grid>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="235*" />
          <ColumnDefinition Width="268*" />
         </Grid.ColumnDefinitions>
          <ListBox x:Name="lb"  SelectionMode="Multiple" Grid.Row="0"  ItemsSource="{Binding MyCollection}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseUp" >
                        <i:InvokeCommandAction CommandParameter="{Binding SelectedItems, ElementName=lb}" Command="{Binding SelectionChangedCommand}"/>
                    </i:EventTrigger>
                </i:Interaction.Triggers>
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <StackPanel>
                            <TextBlock Text="{Binding FirstName}"/>
                            <TextBlock Text="{Binding SecondName}"/>
                            <TextBlock Text="{Binding Company}"/>

                        </StackPanel>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
            <StackPanel Grid.Column="1" >
                <TextBox Grid.Column="1" Height="23" HorizontalAlignment="Left" Text="{Binding SelectedItem,ConverterParameter=FName, Converter={StaticResource Converter}}" Name="textBox1" VerticalAlignment="Top" Width="120" />
                <TextBox Grid.Column="1" Height="23" HorizontalAlignment="Left" Text="{Binding SelectedItem,ConverterParameter=SName, Converter={StaticResource Converter}}" Name="textBox2" VerticalAlignment="Top" Width="120" />
                <TextBox Grid.Column="1" Height="23" HorizontalAlignment="Left" Text="{Binding SelectedItem,ConverterParameter=Comp, Converter={StaticResource Converter}}" Name="textBox3" VerticalAlignment="Top" Width="120" />
            </StackPanel>
       </Grid>
</Window>

C#

    public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                this.DataContext = new ViewModel();
            }

        }

模型

        public class Model : INotifyPropertyChanged
        {
            private string fname;

            public string FirstName
            {
                get { return fname; }
                set { fname = value;RaisePropertyChanged("FirstName"); }
            }

            private string sname;

            public string SecondName
            {
                get { return sname; }
                set { sname = value; RaisePropertyChanged("SecondName");}
            }

            private string company;

            public string Company
            {
                get { return company; }
                set { company = value;RaisePropertyChanged("Company"); }
            }


            public event PropertyChangedEventHandler PropertyChanged;
            private void RaisePropertyChanged(string name)
            {
                if(PropertyChanged!= null)
                {
                    this.PropertyChanged(this,new PropertyChangedEventArgs(name));
                }
            }
        }

视图模型

        public class ViewModel : INotifyPropertyChanged
        {
            private MyCommand selectionChangedCommand;

            public MyCommand SelectionChangedCommand
            {
                get 
                {
                    if (selectionChangedCommand == null)
                    {
                        selectionChangedCommand = new MyCommand(SelectionChanged);
                    }
                    return selectionChangedCommand;
                }
                set { selectionChangedCommand = value; }
            }
            public void SelectionChanged(object value)
            {
                SelectedItem = new ObservableCollection<Model>((value as IEnumerable).OfType<Model>());
            }


            private ObservableCollection<Model> selectedItem;

            public ObservableCollection<Model> SelectedItem
            {
                get { return selectedItem; }
                set { selectedItem = value; RaisePropertyChanged("SelectedItem"); }
            }

            private ObservableCollection<Model> mycoll;

        public ObservableCollection<Model> MyCollection
        {
            get { return mycoll;}
            set { mycoll = value;}
        }
            public ViewModel()
            {
                SelectedItem = new ObservableCollection<Model>();
                SelectedItem.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(SelectedItem_CollectionChanged);
                MyCollection = new ObservableCollection<Model>();
                MyCollection.Add(new Model { FirstName = "aaaaa", SecondName = "bbbbb", Company = "ccccccc" });
                MyCollection.Add(new Model { FirstName = "ddddd", SecondName = "bbbbb", Company = "eeeeeee" });
                MyCollection.Add(new Model { FirstName = "fffff", SecondName = "gggggg", Company = "ccccccc" });

            }

            void SelectedItem_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
            {
                //this.SelectedItem =new ObservableCollection<Model>((sender as ObservableCollection<Model>).Distinct());
            }
            public event PropertyChangedEventHandler PropertyChanged;
            private void RaisePropertyChanged(string name)
            {
                if(PropertyChanged!= null)
                {
                    this.PropertyChanged(this,new PropertyChangedEventArgs(name));
                }
            }
        }
        public class MyCommand : ICommand
        {
            private Action<object> _execute;

            private Predicate<object> _canexecute;

            public MyCommand(Action<object> execute, Predicate<object> canexecute)
            {
                _execute = execute;
                _canexecute = canexecute;
            }

            public MyCommand(Action<object> execute)
                : this(execute, null)
            {
                _execute = execute;
            }

            #region ICommand Members

            public bool CanExecute(object parameter)
            {
                if (parameter == null)
                    return true;
                if (_canexecute != null)
                {
                    return _canexecute(parameter);
                }
                else
                {
                    return true;
                }

            }

            public event EventHandler CanExecuteChanged
            {
                add { CommandManager.RequerySuggested += value; }
                remove { CommandManager.RequerySuggested -= value; }
            }


            public void Execute(object parameter)
            {
                _execute(parameter);
            }

            #endregion
        }

转换器

        public class Converter : IValueConverter
        {
            ObservableCollection<Model> mycollection;
            public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {
                var coll = (ObservableCollection<Model>)value;
                mycollection = coll;
                if (coll.Count == 1)
                {
                    if (parameter.ToString() == "FName")
                        return coll[0].FirstName;
                    else if (parameter.ToString() == "SName")
                        return coll[0].SecondName;
                    else if (parameter.ToString() == "Comp")
                        return coll[0].Company;
                }
                else if(coll.Count >1)
                {
                   // string name = coll[0].FirstName;
                    if (parameter.ToString() == "FName")
                    {
                        string name = coll[0].FirstName;
                        foreach (var c in coll)
                        {
                            if (c.FirstName != name)
                                return null;
                            else continue;
                        }
                        return name;
                    }
                    if (parameter.ToString() == "SName")
                    {
                        string name = coll[0].SecondName;
                        foreach (var c in coll)
                        {
                            if (c.SecondName != name)
                                return null;
                            else continue;
                        }
                        return name;
                    }
                    if (parameter.ToString() == "Comp")
                    {
                        string name = coll[0].Company;
                        foreach (var c in coll)
                        {
                            if (c.Company != name)
                                return null;
                            else continue;
                        }
                        return name;
                    }
                }
                return null;
            }

            public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
            {

                if (parameter.ToString() == "FName")
                {
                    foreach (var c in mycollection)
                    {
                        c.FirstName = value.ToString();
                    }
                    return mycollection;
                }
                else
                if (parameter.ToString() == "SName")
                {
                    foreach (var c in mycollection)
                    {
                        c.SecondName = value.ToString();
                    }
                    return mycollection;
                }
                else
                if (parameter.ToString() == "Comp")
                {
                    foreach (var c in mycollection)
                    {
                        c.Company = value.ToString();
                    }
                    return mycollection;
                }
                return null;
            }
        }