قائمة أزرار الاختيار المرتبطة بالبيانات في WPF

StackOverflow https://stackoverflow.com/questions/805015

  •  03-07-2019
  •  | 
  •  

سؤال

لدي قائمة خيارات في كائن بيانات، وأريد أن أجعلها مكافئة لقائمة أزرار الاختيار للسماح للمستخدم بتحديد خيار واحد فقط منها.تشبه الوظيفة مربع التحرير والسرد المرتبط بالبيانات، ولكن بتنسيق زر الاختيار.

يا سخيفة لي، اعتقدت أنه سيتم بناء هذا، ولكن لا.كيف تقوم بذلك؟

هل كانت مفيدة؟

المحلول

والأساس، وبعد الإطلاع على نتائج جوجل، بدأت مع معلومات من <لأ href = "http://social.msdn.microsoft.com/forums/en-US/wpf/thread/5137aabc-bb3a-478a- 9438-bc93dd9cc0ac / "يختلط =" noreferrer نوفولو "> على موضوع المناقشة MSDN حيث قدم الدكتور WPF جوابا ، الذي يتحدث عن التصميم مربع القائمة للنظر الحق. ومع ذلك، عندما يتم تعطيل مربع القائمة، وكانت الخلفية لون مزعج لانني لم أستطع التخلص من أجل حياة لي، حتى قرأت <لأ href = "http://msdn.microsoft.com/en-us/ مكتبة / ms754242.aspx "يختلط =" نوفولو noreferrer "> على سبيل المثال MSDN من مربع القائمة ControlTemplate ، مما يدل على عنصر الحدود السري الذي كان الركل بعقب خلفيتي.

وهكذا، وهنا كان الجواب النهائي هذا النمط:

<Style x:Key="RadioButtonList" TargetType="{x:Type ListBox}">
    <!-- ControlTemplate taken from MSDN http://msdn.microsoft.com/en-us/library/ms754242.aspx -->
    <Setter Property="SnapsToDevicePixels" Value="true"/>
    <Setter Property="OverridesDefaultStyle" Value="true"/>
    <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
    <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
    <Setter Property="MinWidth" Value="120"/>
    <Setter Property="MinHeight" Value="95"/>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListBox">
                <Border Name="Border" Background="Transparent"
                        BorderBrush="Transparent"
                        BorderThickness="0"
                        CornerRadius="2">
                    <ScrollViewer Margin="0" Focusable="false">
                        <StackPanel Margin="2" IsItemsHost="True" />
                    </ScrollViewer>
                </Border>
                <ControlTemplate.Triggers>
                    <Trigger Property="IsEnabled" Value="false">
                        <Setter TargetName="Border" Property="Background"
                                Value="Transparent" />
                        <Setter TargetName="Border" Property="BorderBrush"
                                Value="Transparent" />
                    </Trigger>
                    <Trigger Property="IsGrouping" Value="true">
                        <Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
                    </Trigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="{x:Type ListBoxItem}" >
                <Setter Property="Margin" Value="2" />
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
                            <Border Name="theBorder" Background="Transparent">
                                <RadioButton Focusable="False" IsHitTestVisible="False"
                                             IsChecked="{TemplateBinding IsSelected}">
                                    <ContentPresenter />
                                </RadioButton>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Setter.Value>
    </Setter>
</Style>

والذي يوفر ControlTemplate ل، والأساليب، مربع القائمة والعناصر. ويعتاد مثل هذا:

<ListBox Grid.Column="1" Grid.Row="0" x:Name="TurnChargeBasedOnSelector" Background="Transparent"
    IsEnabled="{Binding Path=IsEditing}"
    Style="{StaticResource RadioButtonList}"
    ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:MainForm}}, Path=DataContext.RampTurnsBasedOnList}"
    DisplayMemberPath="Description" SelectedValuePath="RampTurnsBasedOnID"
    SelectedValue="{Binding Path=RampTurnsBasedOnID, NotifyOnValidationError=True, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}"/>

وكلما قضيت وقتا مع برنامج الأغذية العالمي، وأكثر وأنا أعتقد أنه يجعل من الصعب تافهة بجنون وصعوبة بجنون تافهة. استمتع. -Scott

نصائح أخرى

ربط مربع القائمة بـ ItemsSource الخاص بـ ListBox مع قائمة الكائنات التي لها خاصية الاسم (يمكن أن يتغير هذا)

<ListBox Name="RadioButtonList">
   <ListBox.ItemTemplate >
        <DataTemplate >
             <RadioButton GroupName="radioList" Tag="{Binding}" Content="{Binding Name}"/>
         </DataTemplate>
                                                    </ListBox.ItemTemplate>
                                                </ListBox>

مهم اسم المجموعة = "قائمة الراديو"

ولقد فعلت ذلك من خلال ValueConverter الذي يحول لenum إلى bool. عن طريق تمرير قيمة التعداد الذي يمثل زر الراديو الخاص بك كما ConverterParameter، المحول بإرجاع ما إذا كان هذا زر الاختيار يجب أن يتم التحقق أم لا.

<Window.Resources>
    <Converters:EnumConverter x:Key="EnumConverter" />
</Window.Resources>

<RadioButton IsChecked="{Binding Path=MyEnum, Mode=TwoWay, 
                                 Converter={StaticResource EnumConverter}, 
                                 ConverterParameter=Enum1}"}
             Content="Enum 1" />
<RadioButton IsChecked="{Binding Path=MyEnum, Mode=TwoWay, 
                                 Converter={StaticResource EnumConverter}, 
                                 ConverterParameter=Enum2}"}
             Content="Enum 2" />

ويعرف EnumConverter كما يلي:

public class EnumConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (targetType.IsAssignableFrom(typeof(Boolean)) && targetType.IsAssignableFrom(typeof(String)))
                throw new ArgumentException("EnumConverter can only convert to boolean or string.");
            if (targetType == typeof(String))
                return value.ToString();

            return String.Compare(value.ToString(), (String)parameter, StringComparison.InvariantCultureIgnoreCase) == 0;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (targetType.IsAssignableFrom(typeof(Boolean)) && targetType.IsAssignableFrom(typeof(String)))
                throw new ArgumentException("EnumConverter can only convert back value from a string or a boolean.");
            if (!targetType.IsEnum)
                throw new ArgumentException("EnumConverter can only convert value to an Enum Type.");

            if (value.GetType() == typeof(String))
            {
                return Enum.Parse(targetType, (String)value, true);
            }

            //We have a boolean, as for binding to a checkbox. we use parameter
            if ((Boolean)value)
                return Enum.Parse(targetType, (String)parameter, true);

            return null;
        }
    }

ملحوظة أنني لا DataBind عناصر إلى قائمة تتضمن التعدادات لتوليد أزرار الاختيار، لقد فعلت لهم باليد. إذا أردت لملء قائمة أزرار الراديو من خلال ملزمة، أعتقد أنك سوف تحتاج إلى تغيير IsChecked ملزمة لMultiBinding الذي يربط كلا من القيمة الحالية والقيمة التعداد في الراديو، لأنه لا يمكنك استخدام ملزمة للConverterParameter.

وسوبر بسيط، MVVM ودية، DataTemplates الاستفادة لأنواع. XAML:

<Window x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfApplication1"
    Title="MainWindow" Height="350" Width="525">

<Window.Resources>
    <DataTemplate DataType="{x:Type local:Option}">
        <RadioButton Focusable="False"
                IsHitTestVisible="False"
                Content="{Binding Display}"
                IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}">
        </RadioButton>
    </DataTemplate>
</Window.Resources>

<Grid>
    <ListBox ItemsSource="{Binding Options}" SelectedItem="{Binding SelectedOption}"/>
</Grid>

وعرض نموذج، وما إلى ذلك:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = new Vm();
    }
}

public class Vm
{
    public Option[] Options { get { return new Option[] { 
        new Option() { Display = "A" }, 
        new Option() { Display = "B" }, 
        new Option() { Display = "C" } }; } }
    public Option SelectedOption { get; set; }
}

public class Option
{
    public string Display { get; set; }
}

إذا التفاف ديك الخيار في نوع معين (أو الأرجح هو بالفعل). يمكنك فقط تحديد DataTemplate لهذا النوع، سوف WPF استخدامها تلقائيا. (تحديد DataTemplate في الموارد مربع القائمة للحد من نطاق حيث سيتم تطبيق DataTemplate).

وأيضا استخدام اسم المجموعة في DataTemplate لضبط مجموعة إذا كنت تريد.

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

وWPF بسيط عندما كنت تعرف كيف.

عذرا، أود أن وضع هذا الرد إلى آخر سكوت O وتعليقا على منصبه، ولكن ليس لدي حتى الآن سمعة للقيام بذلك. أنا حقا أحب جوابه كما كان حلا على غرار الوحيد، وبالتالي لا تتطلب أي وأضاف التعليمات البرمجية الخلفية أو إنشاء السيطرة العرف، وما إلى ذلك.

ولكن، لم دينا قضية واحدة عندما ذهبت بعد ذلك إلى محاولة استخدام الضوابط داخل ListBoxItems. عند استخدام هذا النمط أنا غير قادر على التركيز أي من الضوابط الواردة بسبب هذا السطر:

و<RadioButton Focusable="False" IsHitTestVisible="False" IsChecked="{TemplateBinding IsSelected}">

وزر الراديو يحتاج إلى إيقاف القابل للتركيز وIsHitTestVisible لIsChecked ملزمة للعمل بشكل صحيح. للالتفاف على هذا، لقد غيرت IsChecked من TemplateBinding إلى ملزما العادية، التي سمحت لي لجعله في اتجاهين ملزمة. إزالة إعدادات المخالف أعطاني هذا السطر:

و<RadioButton IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected, Mode=TwoWay}">

والذي يسمح لي الآن أن تركز أي ضوابط الواردة في ListBoxItems كما هو متوقع.

وآمل أن يساعد هذا.

لقد أخذت إلهامي من دخول بلوق جون بنسون, ، لكنه قام بتعديل الحل الخاص به لاستخدام التعدادات التي لها سمة وصف.لذلك أصبحت الأجزاء الرئيسية من الحل:

العداد مع الوصف

public enum AgeRange {
  [Description("0 - 18 years")]
  Youth,
  [Description("18 - 65 years")]
  Adult,
  [Description("65+ years")]
  Senior,
}

رمز لقراءة الأوصاف وإرجاع أزواج المفتاح/القيمة للربط.

public static class EnumHelper
{
    public static string ToDescriptionString(this Enum val)
    {
        var attribute =
            (DescriptionAttribute)
            val.GetType().GetField(val.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false).
                SingleOrDefault();
        return attribute == default(DescriptionAttribute) ? val.ToString() : attribute.Description;
    }

    public static List<KeyValuePair<string,string>> GetEnumValueDescriptionPairs(Type enumType)
    {
        return Enum.GetValues(enumType)
            .Cast<Enum>()
            .Select(e => new KeyValuePair<string, string>(e.ToString(), e.ToDescriptionString()))
            .ToList();
    }
}

موفر بيانات الكائن الخاص بك في XAML

<ObjectDataProvider
    ObjectType="{x:Type local:EnumHelper}"
    MethodName="GetEnumValueDescriptionPairs"
    x:Key="AgeRanges">
    <ObjectDataProvider.MethodParameters>
        <x:Type TypeName="local:AgeRange" />
    </ObjectDataProvider.MethodParameters>
</ObjectDataProvider>

ListBox الخاص بك في XAML

<ListBox 
    ItemsSource="{Binding Source={StaticResource AgeRanges}}"
    SelectedValue="{Binding SelectedAgeRange}"
    SelectedValuePath="Key">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <RadioButton 
                IsChecked="{Binding IsSelected, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"
                Content="{Binding Value}" />
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

الخاصية (مثال:في نموذج العرض الخاص بك) الذي تلتزم به

public class YourViewModel : INotifyPropertyChanged
{
  private AgeRange _selectedAgeRange;
  public AgeRange SelectedAgeRange
  {
    get { return _selectedAgeRange; }
    set 
    {
      if (value != _selectedAgeRange)
      {
        _selectedAgeRange = value;
        OnPropertyChanged("SelectedAgeRange");
      }
    }
  }
}

وأنا للغش:

وكان لي حل لربط مربع القائمة programaticly منذ هذا هو كل ما يبدو للعمل بالنسبة لي:

            if (mUdData.Telephony.PhoneLst != null)
            {
                lbPhone.ItemsSource = mUdData.Telephony.PhoneLst;
                lbPhone.SelectedValuePath = "ID";
                lbPhone.SelectedValue = mUdData.Telephony.PrimaryFaxID;
            }

ووXAML يبدو مثل هذا:

                        <ListBox.ItemTemplate >

                        <DataTemplate >
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition></RowDefinition>
                                </Grid.RowDefinitions>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="Auto"></ColumnDefinition>
                                    <ColumnDefinition Width="Auto"></ColumnDefinition>
                                </Grid.ColumnDefinitions>

                                <RadioButton 
                                    IsChecked="{Binding Path=PrimaryPhoneID}" 
                                    GroupName="Phone" 
                                    x:Name="rbPhone"
                                    Content="{Binding Path=PrimaryPhoneID}"
                                    Checked="rbPhone_Checked"/>

                                <CheckBox Grid.Column="2" IsEnabled="False" IsChecked="{Binding Path=Active}" Content="{Binding Path=Number}" ></CheckBox>

                            </Grid>
                        </DataTemplate>
                    </ListBox.ItemTemplate>

وفي الحدث جهدي لقراءة قيمة زر الاختيار كما يتم تحديده يشبه هذا:

    private void rbPhone_Checked(object sender, RoutedEventArgs e)
    {
        DataRowView dvFromControl = null;
        dvFromControl = (DataRowView)((RadioButton)sender).DataContext;

        BindData.Telephony.PrimaryPhoneID = (int)dvFromControl["ID"];

    }

وعلى أمل أن يساعد شخص ما.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top