簡體   English   中英

使用MVVM C#將WPF ListBox項源控件的UpdateSourceTrigger設置為Explicit

[英]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));
    }
}

假設ListBoxDataContextEmployeesViewModel

<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>

好的,所以我找到了解決方案。 該解決方案不太通用,但是可以通過一些簡單的步驟使其通用。 告訴我這對您是否重要。

警告! 這會有點長...

對於此解決方案,我們將使用以下內容:

  • VisualTreeHelper獲取VisualTree中的所有CheckBox項。 (來源:使用過的THIS解決方案。)
  • 更新列表框中所有復選框的行為。

所以,讓我們開始吧!

幫助者

正如我所說的,我從這個答案中得到了解決方案。

使用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現在需要調用BindingExpressionUpdateSource()方法。 因此,僅通過綁定是不可能實現的。 而且您需要MVVM解決方案,因此您也不需要ViewModel中的任何UI對象。

因此,問題就變成了如何獲取ViewModel中所有綁定表達式的UpdateSource()

以下是我的操作方法。

創建一個ButtonCustomControl ,它將保存您的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM