[c#] MVVM: أزرار الراي الملزمة لنموذج عرض؟


4 Answers

يبدو أنها ثابتة الربط الخاصية IsChecked في .NET 4. يعمل المشروع الذي تم كسره في VS2008 في VS2010.

Question

تحرير: تم إصلاح المشكلة في .NET 4.0.

لقد حاولت ربط مجموعة من أزرار الاختيار بنموذج عرض باستخدام الزر IsChecked . بعد مراجعة مشاركات أخرى ، يبدو أن الخاصية IsChecked ببساطة لا تعمل. لقد وضعت عرضًا توضيحيًا قصيرًا يُعيد إنتاج المشكلة ، وهو ما أدرجته أدناه.

هنا سؤالي: هل هناك طريقة واضحة وموثوقة لربط أزرار الراديو باستخدام MVVM؟ شكر.

معلومات إضافية: لا تعمل الخاصية IsChecked لسببين:

  1. عند تحديد زر ، لا يتم تعيين خصائص IsChecked للأزرار الأخرى في المجموعة إلى false .

  2. عند تحديد زر ، لا يتم تعيين الخاصية IsChecked الخاصة بها بعد المرة الأولى التي يتم فيها تحديد الزر. أنا أظن أن يتم الحصول على سلة المحذوفات بواسطة WPF على النقرة الأولى.

مشروع العرض التوضيحي: إليك الشفرة والترميز للحصول على عرض توضيحي بسيط يعيد إنتاج المشكلة. إنشاء مشروع WPF واستبدال الترميز في Window1.xaml بما يلي:

<Window x:Class="WpfApplication1.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300" Loaded="Window_Loaded">
    <StackPanel>
        <RadioButton Content="Button A" IsChecked="{Binding Path=ButtonAIsChecked, Mode=TwoWay}" />
        <RadioButton Content="Button B" IsChecked="{Binding Path=ButtonBIsChecked, Mode=TwoWay}" />
    </StackPanel>
</Window>

استبدال الرمز في Window1.xaml.cs مع التعليمة البرمجية التالية (الاختراق) ، الذي يعين طراز العرض:

using System.Windows;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            this.DataContext = new Window1ViewModel();
        }
    }
}

الآن إضافة التعليمة البرمجية التالية إلى المشروع باسم Window1ViewModel.cs :

using System.Windows;

namespace WpfApplication1
{
    public class Window1ViewModel
    {
        private bool p_ButtonAIsChecked;

        /// <summary>
        /// Summary
        /// </summary>
        public bool ButtonAIsChecked
        {
            get { return p_ButtonAIsChecked; }
            set
            {
                p_ButtonAIsChecked = value;
                MessageBox.Show(string.Format("Button A is checked: {0}", value));
            }
        }

        private bool p_ButtonBIsChecked;

        /// <summary>
        /// Summary
        /// </summary>
        public bool ButtonBIsChecked
        {
            get { return p_ButtonBIsChecked; }
            set
            {
                p_ButtonBIsChecked = value;
                MessageBox.Show(string.Format("Button B is checked: {0}", value));
            }
        }

    }
}

لإعادة إنشاء المشكلة ، قم بتشغيل التطبيق ثم انقر فوق الزر A. سيظهر مربع رسالة قائلا أنه تم تعيين الخاصية IsChecked "أ" إلى true . الآن حدد الزر B. سيظهر مربع رسالة آخر ، قائلاً أن الخاصية IsChecked الخاصة بـ Button B قد تم تعيينها إلى true ، ولكن لا يوجد مربع رسالة يشير إلى أنه تم تعيين الخاصية IsChecked "أ" إلى false - لم يتم تغيير الخاصية.

الآن انقر فوق الزر A مرة أخرى. سيتم تحديد الزر في النافذة ، ولكن لن يظهر مربع رسالة - لم يتم تغيير الخاصية IsChecked . وأخيرا ، انقر فوق الزر B مرة أخرى - نفس النتيجة. لا يتم تحديث الخاصية IsChecked على الإطلاق لأي زر بعد النقر أولاً على الزر.




يجب عليك إضافة اسم المجموعة لزر الراديو

   <StackPanel>
        <RadioButton Content="Button A" IsChecked="{Binding Path=ButtonAIsChecked, Mode=TwoWay}" GroupName="groupName" />
        <RadioButton Content="Button B" IsChecked="{Binding Path=ButtonBIsChecked, Mode=TwoWay}" GroupName="groupName" />
    </StackPanel>



لدي مشكلة مشابهة جدا في VS2015 و. NET 4.5.1

XAML:

                <ListView.ItemsPanel>
                    <ItemsPanelTemplate>
                        <UniformGrid Columns="6" Rows="1"/>
                    </ItemsPanelTemplate>
                </ListView.ItemsPanel>
                <ListView.ItemTemplate>
                    <DataTemplate >
                        <RadioButton  GroupName="callGroup" Style="{StaticResource itemListViewToggle}" Click="calls_ItemClick" Margin="1" IsChecked="{Binding Path=Selected,Mode=TwoWay}" Unchecked="callGroup_Checked"  Checked="callGroup_Checked">

....

كما ترون في هذا الكود ، لدي قائمة عرض ، والعناصر الموجودة في القالب هي إشارات صوتية تنتمي إلى اسم مجموعة.

إذا أضفت عنصرًا جديدًا إلى المجموعة باستخدام الخاصية ، فسيتم تحديد المجموعة المحددة إلى True ، وستظل بقية الأزرار محددة.

أنا حلها عن طريق الحصول على زر checkbutton أولاً وتعيينه إلى false يدوياً ولكن هذه ليست الطريقة التي من المفترض أن يتم.

كود خلف:

`....
  lstInCallList.ItemsSource = ContactCallList
  AddHandler ContactCallList.CollectionChanged, AddressOf collectionInCall_change
.....
Public Sub collectionInCall_change(sender As Object, e As NotifyCollectionChangedEventArgs)
    'Whenever collection change we must test if there is no selection and autoselect first.   
    If e.Action = NotifyCollectionChangedAction.Add Then
        'The solution is this, but this shouldn't be necessary
        'Dim seleccionado As RadioButton = getCheckedRB(lstInCallList)
        'If seleccionado IsNot Nothing Then
        '    seleccionado.IsChecked = False
        'End If
        DirectCast(e.NewItems(0), PhoneCall).Selected = True
.....
End sub

`




حل واحد هو تحديث ViewModel لأزرار الراديو في إعدادات الخصائص. عند تعيين الزر A إلى True ، اضبط الزر B إلى false.

عامل آخر مهم عند ربط كائن في DataContext هو أن الكائن يجب تطبيق INotifyPropertyChanged. عند تغيير أي خاصية منضم ، يجب أن يتم تشغيل الحدث وتضمين اسم الخاصية التي تم تغييرها. (تم حذف الشيك الفارغ في العينة للإيجاز).

public class ViewModel  : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected bool _ButtonAChecked = true;
    public bool ButtonAChecked
    {
        get { return _ButtonAChecked; }
        set 
        { 
            _ButtonAChecked = value;
            PropertyChanged(this, new PropertyChangedEventArgs("ButtonAChecked"));
            if (value) ButtonBChecked = false;
        }
    }

    protected bool _ButtonBChecked;
    public bool ButtonBChecked
    {
        get { return _ButtonBChecked; }
        set 
        { 
            _ButtonBChecked = value; 
            PropertyChanged(this, new PropertyChangedEventArgs("ButtonBChecked"));
            if (value) ButtonAChecked = false;
        }
    }
}

تصحيح:

وتتمثل المشكلة في أنه عند النقر أولاً فوق الزر B تتغير القيمة IsChecked ويتغذى الربط خلال ، ولكن الزر A لا يتغذى من خلال حالته التي لم يتم تحديدها إلى الخاصية ButtonAChecked. من خلال التحديث يدويًا في التعليمات البرمجية ، سيتم استدعاؤه إلى أداة ضبط الخاصية ButtonAChecked في المرة التالية التي يتم فيها النقر على الزر "أ".




إليك طريقة أخرى يمكنك القيام بها

رأي:

<StackPanel Margin="90,328,965,389" Orientation="Horizontal">
        <RadioButton Content="Mr" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}, Mode=TwoWay}" GroupName="Title"/>
        <RadioButton Content="Mrs" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}, Mode=TwoWay}" GroupName="Title"/>
        <RadioButton Content="Ms" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}, Mode=TwoWay}" GroupName="Title"/>
        <RadioButton Content="Other" Command="{Binding TitleCommand, Mode=TwoWay}" CommandParameter="{Binding Content, RelativeSource={RelativeSource Mode=Self}}" GroupName="Title"/>
        <TextBlock Text="{Binding SelectedTitle, Mode=TwoWay}"/>
    </StackPanel>

ViewModel:

 private string selectedTitle;
    public string SelectedTitle
    {
        get { return selectedTitle; }
        set
        {
            SetProperty(ref selectedTitle, value);
        }
    }

    public RelayCommand TitleCommand
    {
        get
        {
            return new RelayCommand((p) =>
            {
                selectedTitle = (string)p;
            });
        }
    }



Related