[英]Set UpdateSourceTrigger to Explicit for WPF ListBox Item Source Controls using MVVM C#
我有一個Employee Model Class類型的Collection。 它具有兩個屬性empName和isChecked。
期望 :我需要在單擊“應用”按鈕時,從復選框更新屬性isChecked。 否則,不需要更新屬性。
void Main()
{
Dictionary<int, List<Employee>> empList = new Dictionary<int, List<Employee>>()
{
{1, new List<Employee>() { new Employee() {empName = "Raj"}, new Employee() {empName = "Kumar"}}},
{2, new List<Employee>() { new Employee() {empName = "Bala"}}},
{3, new List<Employee>() { new Employee() {empName = "Manigandan"}}},
{4, new List<Employee>() { new Employee() {empName = "Prayag"}, new Employee() {empName = "Pavithran"}}},
{5, new List<Employee>() { new Employee() {empName = "Selva"}}},
};
empList.Dump();
}
public class Employee
{
public string empName { get; set; }
public bool isChecked { get; set; }
}
我使用MVVM方法將此集合綁定到WPF列表框中。 empName與TextBlock綁定,isChecked與Checkbox綁定。
<ListBox ItemsSource="{Binding empList.Values, IsAsync=True, UpdateSourceTrigger=Explicit}">
<cust:BListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding isChecked, UpdateSourceTrigger=Explicit}">
<CheckBox.Content>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding empName, IsAsync=True}" Visibility="Visible" />
</StackPanel>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Apply" Command="{Binding ApplyChangesCommand}"/>
“應用”按鈕的命令為
public ICommand ApplyChangesCommand
{
get
{
return new DelegatingCommand((object param) =>
{
/// Logical Code.
});
}
}
注意:請使用MVVM方法 。
我的建議是:
首先,您應該定義EmployeesViewModel
public class EmployeesViewModel : INotifyPropertyChanged
{
public ObservableCollection<EmployeeViewModel> EmployeeList { get; set; }
public ICommand ApplyChangesCommand;
public EmployeesViewModel()
{
EmployeeList = new ObservableCollection<EmployeeViewModel>
{
new EmployeeViewModel(new Employee {EmpName = "Raj"}),
new EmployeeViewModel(new Employee {EmpName = "Kumar"}),
new EmployeeViewModel(new Employee {EmpName = "Bala"}),
new EmployeeViewModel(new Employee {EmpName = "Manigandan"}),
new EmployeeViewModel(new Employee {EmpName = "Prayag"}),
new EmployeeViewModel(new Employee {EmpName = "Pavithran"}),
new EmployeeViewModel(new Employee {EmpName = "Selva"})
};
ApplyChangesCommand = new DelegatingCommand(ApplyChanges);
}
private void ApplyChanges(object param)
{
foreach(var item in EmployeeList)
{
item.Model.IsChecked = item.IsChecked;
}
}
....
}
此視圖模型包含itemsSource和ListBox
選定項目。 調用ApplyChangesCommand
並更新Employee.IsChecked
時,將調用ApplyChanges(object par)
方法。
您還應該將Employee
包裝到視圖模型中。
public class EmployeeViewModel : INotifyPropertyChanged
{
public Employee Model { get; set; }
private string _empName;
public string EmpName
{
get { return _empName;}
set
{
_empName = value;
OnPropertyChanged();
}
}
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked;}
set
{
_isChecked = value;
OnPropertyChanged();
}
}
public EmployeeViewModel(Employee model)
{
Model = model;
IsChecked = model.IsChecked;
EmpName = model.EmpName;
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
假設ListBox
的DataContext
是EmployeesViewModel
。
<ListBox ItemsSource="{Binding EmployeeList, IsAsync=True, UpdateSourceTrigger=Explicit}" SelectedItem="{Binding SelectedItem}">
<cust:BListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked, UpdateSourceTrigger=Explicit}">
<CheckBox.Content>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding EmpName, IsAsync=True}" Visibility="Visible" />
</StackPanel>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
好的,所以我找到了解決方案。 該解決方案不太通用,但是可以通過一些簡單的步驟使其通用。 告訴我這對您是否重要。
警告! 這會有點長...
對於此解決方案,我們將使用以下內容:
所以,讓我們開始吧!
正如我所說的,我從這個答案中得到了解決方案。
使用FindVisualChildren方法創建一個幫助器類:
public static class VisualHelper
{
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject dependencyObject) where T: DependencyObject
{
if (dependencyObject == null)
yield break;
int totalChildrenAmount = VisualTreeHelper.GetChildrenCount(dependencyObject);
for (int childIndex = 0; childIndex < totalChildrenAmount; childIndex++)
{
DependencyObject child = VisualTreeHelper.GetChild(dependencyObject, childIndex);
if (child is T)
{
yield return (T)child;
}
foreach (T deeperChild in FindVisualChildren<T>(child))
{
yield return deeperChild;
}
}
}
}
此方法將幫助我們獲取ListBox控件下的所有CheckBox。
ApplyAllCheckBoxBindingsInListBoxBehavior 。 我知道這個名字很長,但是由於我們需要非常具體的名稱,因此我強烈建議使用一個長名字,以明確行為的含義。
自恕我直言,由於命令是從ViewModel初始化的,所以我已將其從Command切換為行為,該命令不應具有對視覺的引用(控件等),並且解決方案基於訪問視覺控件。
聊夠了,這是行為:
public class ApplyAllCheckBoxBindingsInListBoxBehavior
{
public static ListBox GetListBox(DependencyObject obj)
{
return (ListBox)obj.GetValue(ListBoxProperty);
}
public static void SetListBox(DependencyObject obj, ListBox value)
{
obj.SetValue(ListBoxProperty, value);
}
// Using a DependencyProperty as the backing store for ListBox. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ListBoxProperty =
DependencyProperty.RegisterAttached("ListBox", typeof(ListBox), typeof(ApplyBindingsBehavior), new PropertyMetadata(null, ListBoxChanged));
private static void ListBoxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Button button = d as Button;
if (button == null)
return;
button.Click -= OnClick;
button.Click += OnClick;
}
private static void OnClick(object sender, RoutedEventArgs routedEventArgs)
{
ListBox lb = GetListBox(sender as Button);
IEnumerable<CheckBox> allCBs = VisualHelper.FindVisualChildren<CheckBox>(lb);
foreach (CheckBox checkBox in allCBs)
{
checkBox.GetBindingExpression(CheckBox.IsCheckedProperty).UpdateSource();
}
}
}
該行為將在XAML中的按鈕上設置:
<ListBox Name="EmpList" ItemsSource="{Binding empList.Values, IsAsync=True, UpdateSourceTrigger=Explicit}">
<cust:BListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding isChecked, UpdateSourceTrigger=Explicit}">
<CheckBox.Content>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding empName, IsAsync=True}" Visibility="Visible" />
</StackPanel>
</CheckBox.Content>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Content="Apply" behaviors:ApplyAllCheckBoxBindingsInListBoxBehavior.ListBox="{Binding ElementName=EmpList}"/>
請注意,我已經為ListBox添加了名稱,並用行為替換了按鈕上的Command。
如前所述,行為非常具體。 這是為了使解決方案更簡單。 如果您想要更一般的行為,則需要對行為,XAML和幫助程序進行一些修改。
首先,使此解決方案起作用。 如果它可行,並且您仍想使其更通用,請告訴我,我們將很樂意為您提供幫助!
編碼愉快! :)
真正的問題是UpdateSourceTrigger=Explicit
現在需要調用BindingExpression
的UpdateSource()
方法。 因此,僅通過綁定是不可能實現的。 而且您需要MVVM解決方案,因此您也不需要ViewModel中的任何UI對象。
因此,問題就變成了如何獲取ViewModel中所有綁定表達式的UpdateSource()
?
以下是我的操作方法。
創建一個Button
的CustomControl
,它將保存您的BindingExpression
,稍后我們可以將Command
作為CommandParameter
傳遞給Command
:
public class MyButton : Button
{
private List<BindingExpression> bindings;
public List<BindingExpression> Bindings
{
get
{
if (bindings == null)
bindings = new List<BindingExpression>();
return bindings;
}
set { bindings = value; }
}
}
命令:
public RelayCommand ApplyChangesCommand { get; set; }
public void ApplyChangesCommandAction(object param)
{
foreach (var item in (param as List<BindingExpression>))
{
item.UpdateSource();
}
}
命令綁定:
<local:MyButton x:Name="MyButton" Content="Apply" Command="{Binding ApplyChangesCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=Bindings}"/>
最后,棘手的部分(我們可以/將要辯論)將您所有的CheckBox
綁定與Button's
Binding集合相關聯。 我已經創建了一個Converter
來做到這一點(並不是將任何command
綁定到Checkboxes
),您可以使用任何其他事件/行為等:
復選框的命令綁定:
<CheckBox.Command>
<MultiBinding Converter="{StaticResource Converter}">
<Binding RelativeSource="{RelativeSource Self}" Path="." />
<Binding ElementName="MyButton" Path="Bindings" />
</MultiBinding>
</CheckBox.Command>
轉換器:
public class Converter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
CheckBox FE = values[0] as CheckBox;
List<BindingExpression> bindings = values[1] as List<BindingExpression>;
bindings.Add(FE.GetBindingExpression(CheckBox.IsCheckedProperty));
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
而且一切正常。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.