我有一个特定的场景。我的申请看起来像这样。
在左侧有一些用户列表,它是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的两个位置。
答案 0 :(得分:2)
我完全同意@GazTheDestroyer ...这种数据绑定不能仅通过数据绑定来实现。 @Kumar所建议的是作为POC工作,但是当你在一个实时项目中并且使用模型,viewModel和视图以及许多具有一个视图模型的UserControl或一个具有两个ViewModel的用户控件时,则难以实现此场景无法猜测。
好的,没有理论了。我已经实现了这一点,我将分享我是如何做到的。
一对一的DataBinding非常完美且工作正常。当您选择用户4时,此用户Note
字段和Field3 Editable NoteBox
绑定到同一属性,因此它可以正常工作。
在多个选择中首先选择User4
,然后选择User3
和user1
,当选择多个项目时,我在代码中加入逻辑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;
}
}