dro*_*ned 25 c# wpf combobox mvvm
我的WPF应用程序中有一个组合框:
<ComboBox ItemsSource="{Binding CompetitorBrands}" DisplayMemberPath="Value"
SelectedValuePath="Key" SelectedValue="{Binding Path=CompMfgBrandID, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" Text="{Binding CompMFGText}"/>
Run Code Online (Sandbox Code Playgroud)
绑定到的集合 KeyValuePair<string, string>
这是我的ViewModel中的CompMfgBrandID属性:
public string CompMfgBrandID
{
get { return _compMFG; }
set
{
if (StockToExchange != null && StockToExchange.Where(x => !string.IsNullOrEmpty(x.EnteredPartNumber)).Count() > 0)
{
var dr = MessageBox.Show("Changing the competitor manufacturer will remove all entered parts from the transaction. Proceed?",
"Transaction Type", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
if (dr != DialogResult.Yes)
return;
}
_compMFG = value;
StockToExchange.Clear();
...a bunch of other functions that don't get called when you click 'No'...
OnPropertyChanged("CompMfgBrandID");
}
}
Run Code Online (Sandbox Code Playgroud)
如果选择"是",则表现如预期.清除项目并调用其余功能.如果我选择"否",它将返回并且不会清除我的列表或调用任何其他功能,这很好,但组合框仍然显示新选择.当用户选择"否"时,我需要它恢复到原始选择,就像没有任何改变一样.我怎么能做到这一点?我也尝试添加代码e.Handled = true隐藏,但无济于事.
Dvo*_*nik 21
.NET 4.5.1+的非常简单的解决方案:
<ComboBox SelectedItem="{Binding SelectedItem, Delay=10}" ItemsSource="{Binding Items}" />
Run Code Online (Sandbox Code Playgroud)
在大多数情况下,它对我有用.您可以在组合框中回滚选择,只需在没有赋值的情况下触发NotifyPropertyChanged.
spl*_*tor 18
这可以使用Blend的通用行为以通用和紧凑的方式实现.
该行为定义了一个名为的依赖项属性SelectedItem,您应该将绑定放在此属性中,而不是放在ComboBox的SelectedItem属性中.该行为负责将依赖项属性中的更改传递给ComboBox(或更一般地,传递给Selector),并且当Selector SelectedItem更改时,它会尝试将其分配给自己的SelectedItem属性.如果赋值失败(可能是因为绑定的VM proeprty setter拒绝了赋值),则该行为会SelectedItem使用其SelectedItem属性的当前值更新Selector .
出于各种原因,您可能会遇到清除选择器中的项列表并且所选项变为空的情况(请参阅此问题).在这种情况下,您通常不希望VM属性变为null.为此,我添加了IgnoreNullSelection依赖项属性,默认情况下为true.这应该可以解决这个问题.
这是CancellableSelectionBehavior班级:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Interactivity;
namespace MySampleApp
{
internal class CancellableSelectionBehavior : Behavior<Selector>
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.SelectionChanged += OnSelectionChanged;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.SelectionChanged -= OnSelectionChanged;
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register("SelectedItem", typeof(object), typeof(CancellableSelectionBehavior),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnSelectedItemChanged));
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty IgnoreNullSelectionProperty =
DependencyProperty.Register("IgnoreNullSelection", typeof(bool), typeof(CancellableSelectionBehavior), new PropertyMetadata(true));
/// <summary>
/// Determines whether null selection (which usually occurs since the combobox is rebuilt or its list is refreshed) should be ignored.
/// True by default.
/// </summary>
public bool IgnoreNullSelection
{
get { return (bool)GetValue(IgnoreNullSelectionProperty); }
set { SetValue(IgnoreNullSelectionProperty, value); }
}
/// <summary>
/// Called when the SelectedItem dependency property is changed.
/// Updates the associated selector's SelectedItem with the new value.
/// </summary>
private static void OnSelectedItemChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var behavior = (CancellableSelectionBehavior)d;
// OnSelectedItemChanged can be raised before AssociatedObject is assigned
if (behavior.AssociatedObject == null)
{
System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
var selector = behavior.AssociatedObject;
selector.SelectedValue = e.NewValue;
}));
}
else
{
var selector = behavior.AssociatedObject;
selector.SelectedValue = e.NewValue;
}
}
/// <summary>
/// Called when the associated selector's selection is changed.
/// Tries to assign it to the <see cref="SelectedItem"/> property.
/// If it fails, updates the selector's with <see cref="SelectedItem"/> property's current value.
/// </summary>
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (IgnoreNullSelection && (e.AddedItems == null || e.AddedItems.Count == 0)) return;
SelectedItem = AssociatedObject.SelectedItem;
if (SelectedItem != AssociatedObject.SelectedItem)
{
AssociatedObject.SelectedItem = SelectedItem;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是在XAML中使用它的方法:
<Window x:Class="MySampleApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="My Smaple App" Height="350" Width="525"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MySampleApp"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance local:MainWindowViewModel}">
<StackPanel>
<ComboBox ItemsSource="{Binding Options}">
<i:Interaction.Behaviors>
<local:CancellableSelectionBehavior SelectedItem="{Binding Selected}" />
</i:Interaction.Behaviors>
</ComboBox>
</StackPanel>
</Window>
Run Code Online (Sandbox Code Playgroud)
这是VM属性的示例:
private string _selected;
public string Selected
{
get { return _selected; }
set
{
if (IsValidForSelection(value))
{
_selected = value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
WPF*_*-it 17
在MVVM下实现这一目标....
1]具有处理SelectionChangedComboBox事件的附加行为.这个事件是通过一些带有Handled标志的事件args引发的.但将其设置为true对于SelectedValue绑定无用.无论事件是否被处理,绑定都会更新源.
2]因此我们将ComboBox.SelectedValue绑定配置为TwoWay和Explicit.
3]只有当您的支票满意并且消息框说明Yes是我们执行时BindingExpression.UpdateSource().否则,我们只需调用BindingExpression.UpdateTarget()以恢复旧的选择.
在下面的示例中,我有一个KeyValuePair<int, int>绑定到窗口数据上下文的列表.它ComboBox.SelectedValue被绑定到一个简单的可写MyKey属性Window.
XAML ......
<ComboBox ItemsSource="{Binding}"
DisplayMemberPath="Value"
SelectedValuePath="Key"
SelectedValue="{Binding MyKey,
ElementName=MyDGSampleWindow,
Mode=TwoWay,
UpdateSourceTrigger=Explicit}"
local:MyAttachedBehavior.ConfirmationValueBinding="True">
</ComboBox>
Run Code Online (Sandbox Code Playgroud)
MyDGSampleWindowx:名称在哪里Window?
代码背后......
public partial class Window1 : Window
{
private List<KeyValuePair<int, int>> list1;
public int MyKey
{
get; set;
}
public Window1()
{
InitializeComponent();
list1 = new List<KeyValuePair<int, int>>();
var random = new Random();
for (int i = 0; i < 50; i++)
{
list1.Add(new KeyValuePair<int, int>(i, random.Next(300)));
}
this.DataContext = list1;
}
}
Run Code Online (Sandbox Code Playgroud)
和附加的行为
public static class MyAttachedBehavior
{
public static readonly DependencyProperty
ConfirmationValueBindingProperty
= DependencyProperty.RegisterAttached(
"ConfirmationValueBinding",
typeof(bool),
typeof(MyAttachedBehavior),
new PropertyMetadata(
false,
OnConfirmationValueBindingChanged));
public static bool GetConfirmationValueBinding
(DependencyObject depObj)
{
return (bool) depObj.GetValue(
ConfirmationValueBindingProperty);
}
public static void SetConfirmationValueBinding
(DependencyObject depObj,
bool value)
{
depObj.SetValue(
ConfirmationValueBindingProperty,
value);
}
private static void OnConfirmationValueBindingChanged
(DependencyObject depObj,
DependencyPropertyChangedEventArgs e)
{
var comboBox = depObj as ComboBox;
if (comboBox != null && (bool)e.NewValue)
{
comboBox.Tag = false;
comboBox.SelectionChanged -= ComboBox_SelectionChanged;
comboBox.SelectionChanged += ComboBox_SelectionChanged;
}
}
private static void ComboBox_SelectionChanged(
object sender, SelectionChangedEventArgs e)
{
var comboBox = sender as ComboBox;
if (comboBox != null && !(bool)comboBox.Tag)
{
var bndExp
= comboBox.GetBindingExpression(
Selector.SelectedValueProperty);
var currentItem
= (KeyValuePair<int, int>) comboBox.SelectedItem;
if (currentItem.Key >= 1 && currentItem.Key <= 4
&& bndExp != null)
{
var dr
= MessageBox.Show(
"Want to select a Key of between 1 and 4?",
"Please Confirm.",
MessageBoxButton.YesNo,
MessageBoxImage.Warning);
if (dr == MessageBoxResult.Yes)
{
bndExp.UpdateSource();
}
else
{
comboBox.Tag = true;
bndExp.UpdateTarget();
comboBox.Tag = false;
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
在行为中,我使用ComboBox.Tag属性临时存储一个标志,当我们恢复到旧的选定值时,该标志会跳过重新检查.
如果这有帮助,请告诉我.