DataBinding在MVVM模式中使用combobox的问题。 [英] DataBinding Issue with combobox in MVVM pattern.
问题描述
我正在使用MVVM开发WPF技术。我在绑定组合框时遇到问题。有一对多的关系。当我选择单个值时,我想在组合框中加载相关值。
***There is one to many relationship. When I select the single value i want to load related values in combo box***
**Following is view Model**
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Homeopathic_Healing_Zone.Data;
using Homeopathic_Healing_Zone.Desktop.Services;
namespace Homeopathic_Healing_Zone.Desktop.PatientSign
{
class EditPatientSignViewModel : BindableBase
{
private ISignRepository _repo;
public EditPatientSignViewModel(ISignRepository repo)
{
_repo = repo;
}
private ObservableCollection<SignsValue> _signsValues;
public ObservableCollection<SignsValue> SignsValue
{
get { return _signsValues; }
set { SetProperty(ref _signsValues, value); }
}
private SignsValue _selectSignsValue;
public SignsValue selectSignsValue
{
get { return _selectSignsValue; }
set { SetProperty(ref _selectSignsValue, value); }
}
private SimpleEditableSign _sign;
public SimpleEditableSign Sign
{
get { return _sign; }
set { SetProperty(ref _sign, value); }
}
private Sign _editingSign = null;
public void SetSign(Sign sin)
{
_editingSign = sin;
if (Sign != null) Sign.ErrorsChanged -= RaiseCanExecuteChanged;
Sign = new SimpleEditableSign();
Sign.ErrorsChanged += RaiseCanExecuteChanged;
CopySign(sin, Sign);
}
private void RaiseCanExecuteChanged(object sender, EventArgs e)
{
SaveCommand.RaiseCanExecuteChanged();
}
private async void OnSave()
{
UpdateSign(Sign, _editingSign);
await _repo.AddSignAsync(_editingSign);
// System.Threading.Thread.Sleep(5000);
//Done();
}
private bool CanSave()
{
return !Sign.HasErrors;
// return true;
}
private void OnCancel()
{
Done();
}
private async void CopySign(Sign source, SimpleEditableSign target)
{
target.Id = source.SignId;
target.Name = source.Name;
//target.ValueName = source.SignsValues.Equals(target.Id,
source.SignId);
//var x = source.SignsValues.Where(v => v.SignId ==
target.Id).ToString();
SignsValue = new ObservableCollection<SignsValue>(
await _repo.GetSignsWithSignsValuesAsync(source.SignId)
);
}
public void LoadSignValues(Sign a)
{
}
}
}
**following is xmal file of view**
<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Homeopathic_Healing_Zone.Desktop.PatientSign"
xmlns:Data="clr-namespace:Homeopathic_Healing_Zone.Data;assembly=Homeopathic-Healing-Zone.Data"
x:Class="Homeopathic_Healing_Zone.Desktop.PatientSign.EditPatientSignView"
mc:Ignorable="d"
d:DesignHeight="450"
d:DesignWidth="800"
Loaded="UserControl_Loaded">
<Grid >
<Grid x:Name="Grid1"
DataContext="{Binding Sign}"
HorizontalAlignment="Left"
Margin="84,85,0,0"
VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Label Content="Name:"
Grid.Column="0"
HorizontalAlignment="Left"
Margin="3"
Grid.Row="0"
VerticalAlignment="Center" />
<TextBox x:Name="NameTextBox"
Grid.Column="1"
HorizontalAlignment="Left"
Height="23"
Margin="3"
Grid.Row="0"
Text="{Binding Name, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"
VerticalAlignment="Center"
Width="120" />
</Grid>
<ComboBox HorizontalAlignment="Left"
Margin="424,85,0,0"
VerticalAlignment="Top"
Width="159"
ItemsSource="{Binding}"
DisplayMemberPath="Name"
SelectedValuePath="SignId"
/>
</Grid>
</UserControl>
推荐答案
Hi M.NISAR,
Hi M.NISAR,
这里有一个简单的例子演示如何同步两个组合框,考虑一下以下ViewModel:
here is a simple example demonstrating how to synchronize two combo box, consider the following ViewModel:
namespace OneToManyComboBox
{
public class Parent
{
public int ParentId { get; set; }
public string Name { get; set; }
public List<Child> Many { get; set; }
}
public class Child
{
public int ChildId { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
}
public class MainWindowViewModel: ObservableObject
{
public ObservableCollection<Parent> Parents { get; set; }
public MainWindowViewModel()
{
Parents = new ObservableCollection<Parent>(new List<Parent>() {
new Parent
{ ParentId=1, Name="test1",
Many = new List<Child>
{
new Child{ ChildId = 1, Name = "Parent1-Child1", ParentId = 1},
new Child{ ChildId = 2, Name = "Parent1-Child2", ParentId = 1},
new Child{ ChildId = 3, Name = "Parent1-Child3", ParentId = 1}
}
},
new Parent
{ ParentId=1, Name="test2",
Many = new List<Child>
{
new Child{ ChildId = 1, Name = "Parent2-Child1", ParentId = 2},
new Child{ ChildId = 2, Name = "Parent2-Child2", ParentId = 2},
new Child{ ChildId = 3, Name = "Parent2-Child3", ParentId = 2}
}
}
});
}
}
}
此视图的XAML:
<Window x:Class="OneToManyComboBox.MainWindow"
....>
<Window.DataContext>
<local:MainWindowViewModel x:Name="vm"/>
</Window.DataContext>
<Grid>
<ComboBox x:Name="ParentsCombobox" HorizontalAlignment="Left" Margin="30,90,0,0" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Parents}" DisplayMemberPath="Name">
</ComboBox>
<ComboBox HorizontalAlignment="Left" Margin="176,90,0,0" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding ElementName=ParentsCombobox, Path= SelectedItem.Many}"
DisplayMemberPath="Name"/>
</Grid>
</Window>
查看两个组合框上ItemsSource的绑定:
take a look at the bindings for the ItemsSource on the two combo boxes:
<ComboBox x:Name="ParentsCombobox"
ItemsSource="{Binding Parents}" DisplayMemberPath="Name">
</ComboBox>
<ComboBox
ItemsSource="{Binding ElementName=ParentsCombobox, Path= SelectedItem.Many}"
DisplayMemberPath="Name"/>
这假设每个父母都包含相关孩子的列表(比如说
DDD中的聚合根),否则,逻辑应该被调整,并且将取决于将在SelectionChanged上触发的命令,让我们来看看ViewModel:
this assuming that each parent contains a list of related children (let say aggregate root in DDD), otherwise, the logic should be adapted, and will depend on a command that will be triggered on SelectionChanged, let's take a look to the ViewModel:
public class MainWindowViewModel: ObservableObject
{
public ObservableCollection<Parent> Parents { get; set; }
public ObservableCollection<Child> Many { get; set; }
public DelegateCommand<Parent> ParentChangedCommand { get; set; }
private List<Child> _related;
public MainWindowViewModel()
{
_related = new List<Child>
{
new Child{ ChildId = 1, Name = "Parent1-Child1", ParentId = 1},
new Child{ ChildId = 2, Name = "Parent1-Child2", ParentId = 1},
new Child{ ChildId = 3, Name = "Parent1-Child3", ParentId = 1},
new Child{ ChildId = 1, Name = "Parent2-Child1", ParentId = 2},
new Child{ ChildId = 2, Name = "Parent2-Child2", ParentId = 2},
new Child{ ChildId = 3, Name = "Parent2-Child3", ParentId = 2}
};
Parents = new ObservableCollection<Parent>(new List<Parent>() {
new Parent { ParentId=1, Name="test1" },
new Parent { ParentId=2, Name="test2" }
});
Many = new ObservableCollection<Child>(_related);
ParentChangedCommand = new DelegateCommand<Parent>((p) => {
Many = new ObservableCollection<Child>(_related.Where(c => c.ParentId == p.ParentId).ToList());
RaisePropertyChanged(nameof(Many));
});
}
}
这一次我们有两个集合 只有ParentId作为子节点中的一个键,我们还有一个新的命令可以处理更改的父选择,这里是委托处理程序的代码:
this time we have two collections with just the ParentId as a key in the child one, also we have a new command which will handle the parent selection changed, here is the code for the delegate handler:
public class DelegateCommand<T> : ICommand
where T: class
{
private Action<T> _action;
private Func<T, bool> _predicate;
public DelegateCommand(Action<T> action, Func<T, bool> predicate = null)
{
_action = action;
_predicate = predicate;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return _predicate?.Invoke((T)parameter) ?? true;
}
public void Execute(object parameter)
{
_action.Invoke((T)parameter);
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, new EventArgs());
}
}
最后一部分涉及使用表达式混合的交互命名空间:
the last part involves the usage of Interactivity namespace of expression blend:
Install-Package Microsoft.SDK.Expression.Blend
和Xaml你应该声明命名空间:
and in the Xaml you should declare the namespace:
<Window
....
xmlns:i="clr-namespace:System.Windows.Interactivity;namespace=System.Windows.Interactivity"
....>
<Window.DataContext>
<local:MainWindowViewModel x:Name="vm"/>
</Window.DataContext>
<Grid>
<ComboBox x:Name="ParentsCombobox" HorizontalAlignment="Left" Margin="30,90,0,0" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Parents}" DisplayMemberPath="Name">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding ParentChangedCommand}" CommandParameter="{Binding ElementName=ParentsCombobox, Path=SelectedItem}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
<ComboBox HorizontalAlignment="Left" Margin="176,90,0,0" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Many, Mode=TwoWay,BindsDirectlyToSource=True}"
DisplayMemberPath="Name"/>
</Grid>
Expression Blend Interactivity库让我们可以在框架元素上使用触发器和行为。
the Expression Blend Interactivity library gives us the possibility to use triggers and behaviors on framework elements.
最诚挚的问候,
Mouad。
良好的编码;
这篇关于DataBinding在MVVM模式中使用combobox的问题。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!