Frage

Ich habe eine Liste von Optionen in einem Datenobjekt, und ich möchte die äquivalent ein Optionsfeld Liste machen, damit der Benutzer einen auswählen und nur einen von ihnen. Funktionalität ähnlich eine Databound Combo-Box, aber in Optionsfeld Format.

Dumme mich, ich dachte, das eingebaut werden würde, aber nein. Wie machen Sie das?

War es hilfreich?

Lösung

Im Grunde genommen, nachdem die Google-Ergebnisse bewerten, ich mit der Info begann von eine MSDN-Diskussion, wo Dr. WPF eine Antwort zur Verfügung gestellt, die über Styling ein ListBox spricht Recht zu suchen. Wenn jedoch die listbox deaktiviert ist, der Hintergrund war eine ärgerliche Farbe, die ich nicht für das Leben von mir loswerden konnte, bis wir lesen das MSDN Beispiel des ListBox-Control , die das Geheimnis Border Element zeigt, dass mein Hintergrund Hintern kickte.

Also, die endgültige Antwort hier war dieser Stil:

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

, die eine Control für und Design bietet, die List und die Gegenstände. Und es wird wie folgt verwendet:

<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}"/>

Je mehr ich verbringe Zeit mit WPF, desto mehr denke ich, es die triviale irrsinnig schwierig und die irrsinnig schwierig trivial macht. Genießen. -Scott

Andere Tipps

Binden Sie das Listenfeld auf die Itemssource eines List-Box mit einer Liste von Objekten, die eine Eigenschaft Name (dies kann sich ändern)

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

wichtig Gruppenname = "radioList"

Ich habe dies durch ein ValueConverter getan, die eine enum zu einem bool umwandelt. Durch das Bestehen der ENUM-Wert, die Ihre Radiobutton als ConverterParameter darstellt, gibt der Konverter, ob diese Eigenschaft aktiviert werden soll oder nicht.

<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 ist wie folgt definiert:

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

Beachten Sie, dass ich auf die Liste der Aufzählungen Databind nicht die Radio-Buttons zu erzeugen, ich habe sie von Hand. Wenn Sie die Liste der Radiobuttons durch eine Bindung füllen wollten, ich glaube, Sie werden die IsChecked ändern müssen, um zu einem MultiBinding Bindung, die sowohl auf den aktuellen Wert bindet und das ENUM-Wert des Radios, da Sie keine bindende auf ConverterParameter verwenden können.

Super Einfach, mvvm freundlich, nutzt Datatemplates für Typen. 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>

Ansicht Modell, etc:

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

Wenn Sie Ihre Wahl in einem bestimmten Typ wickeln (oder wahrscheinlich ist es bereits). Sie können nur eine Datatemplate für diese Art festgelegt, WPF automatisch verwenden. (Define Datatemplate in ListBox Ressourcen den Umfang zu begrenzen, wo die Datatemplate angewandt wird).

Auch Gruppennamen in der Datatemplate verwenden, um die Gruppe zu setzen, wenn Sie wollen.

Das ist viel einfacher als die Steuervorlage zu ändern, aber es bedeutet, dass Sie auf ausgewählte Artikel eine blaue Linie zu bekommen. (Wieder nichts ein bisschen Styling nicht beheben kann).

WPF ist einfach, wenn man weiß wie.

Sorry, würde Ich mag diese Antwort auf Scott O Beitrag setzen als Kommentar zu seinem Beitrag, aber ich habe noch nicht den Ruf, das zu tun. Ich mochte seine Antwort, wie es ein stil einzige Lösung war und daher nicht verlangen, jeden hinzugefügten Code-behind oder ein benutzerdefiniertes-Steuerelement erstellen, etc.

Allerdings habe ich ein Problem habe, wenn ich dann versuchen, ging zu den Kontrollen in den ListBoxItems verwenden. Wenn ich diesen Stil verwenden kann ich nicht auf diese Linie durch eine der enthaltenen Steuerelemente konzentrieren:

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

Der Radiobutton muss Fokussierbare und IsHitTestVisible für die IsChecked auszuschalten Bindung korrekt zu arbeiten. Um dies zu umgehen, änderte es den IsChecked von einem Template zu einer regelmäßigen Bindung, die mir erlaubt, es ist eine Zwei-Wege-Bindung zu machen. Das Entfernen der problematischen Einstellungen gab mir diese Zeile:

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

Welche nun erlaubt mir, alle Steuerelemente in ListBoxItems enthalten sind, zu konzentrieren, wie erwartet.

Hope, das hilft.

Ich habe meine Inspiration von Jon Benson Blog Eintrag , aber modifiziert seine Lösung Aufzählungen zu verwenden, die eine Beschreibung Attribut haben. So sind die wichtigsten Teile der Lösung wurde:

Enumerator mit Beschreibungen

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

Code-zum Lesen Beschreibungen und Schlüssel / Wert-Paare Rückkehr zur Bindung.

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();
    }
}

Ihre Objektdatenprovider in XAML

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

Ihre ListBox in 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>

Die Eigenschaft (zum Beispiel in Ihrer Ansicht nach Modell), die Sie binden an

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

ich betrogen:

Meine Lösung war, um das Listenfeld zu binden programaticly denn das alles ist, was für mich schien zu funktionieren:

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

Die XAML sieht wie folgt aus:

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

Und in meinem Fall den Wert des Optionsfelds zu lesen, wie es sieht wie folgt gewählt:

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

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

    }

Hoffnung, dass jemand hilft.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top