Laden...

[Gelöst] Wie kann ich ein RadioButton "IsChecked" an String Property via IValueConverter binden?

Erstellt von Kilian vor 3 Jahren Letzter Beitrag vor 3 Jahren 640 Views
K
Kilian Themenstarter:in
7 Beiträge seit 2019
vor 3 Jahren
[Gelöst] Wie kann ich ein RadioButton "IsChecked" an String Property via IValueConverter binden?

Hallo Zusammen,

wie im Titel schon erwähnt möchte ich die IsChecked Eigenschaft mittels IValueConverter an einen String binden.

Im Laufe dieses Programms kommt der User auf ein "Sub-Menü" (Window) wo er unter anderem zwei RadioButtons mit der Auswahl "JA/NEIN" findet.

Schließt der User nun das Sub-Menü und öffnet es anschließend wieder, weil er z.B. eine Eintragung vergessen hat, sollen natürlich seine bisherigen Einstellungen erhalten bleiben.

Wird zum Beispiel "JA" angeklickt, das Menü geschlossen und wieder geöffnet, springt der Konverter nachdem die IsChecked Eigenschaft auf true gesetzt wird auf "ConvertBack" mit falschen Angaben (false) und überschreibt das "X" mit "null", somit ist keiner der beiden RadioButtons selektiert.

Das Gleiche wende ich auch bei einer CheckBox an und dort funktioniert es tadellos.

Der String wird direkt nach "InitializeComponent()" an die "IsChecked" Eigenschaft gebunden.

Für eure Hilfe bedanke ich mich schon einmal im Voraus. 😁

Konverter:


[ValueConversion(typeof(string), typeof(bool))]
    public class StringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return false;
            return true;            
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null) return null;
            if ((bool)value == true) return "X";

            return null;
        }
    }

XAML:


<RadioButton Content="JA"
                                    Grid.Column="1"                                     
                                    GroupName="Point_6_1"
                                    IsChecked="{Binding Point_6_1.Yes, Mode=TwoWay, Converter={StaticResource StringConverter}}"/>

W
955 Beiträge seit 2010
vor 3 Jahren

... das müsste jemand nach WPF verschieben.

5.657 Beiträge seit 2006
vor 3 Jahren

Zeig mal den Code für die Point_6_1.Yes-Eigenschaft.

Ansonsten kannst du dir mit dem Debugger anschauen, was genau dort passiert: [Artikel] Debugger: Wie verwende ich den von Visual Studio?

@witte: Danke für den Hinweis
@Kilian: Bitte beim nächsten Mal darauf achten, gleich im richtigen Forum zu posten. Das erhöht die Wahrscheinlichkeit auf eine korrekte Antwort 😃

PS: Dieser Code:

if (value == null) return false;
return true;

läßt sich auf folgende Zeile reduzieren:

return (value != null);

Siehe auch [Tipp] Anfängerhinweis == true / == false

Weeks of programming can save you hours of planning

K
Kilian Themenstarter:in
7 Beiträge seit 2019
vor 3 Jahren

Erst einmal Danke für eure Antworten 🙂

@witte Gibt es noch weitere Foren für WPF? GUI: WPF und XAML erschien mir das Richtige Forum zu sein. Gib mir Bescheid, damit ich beim nächsten Mal gleich im richtigen Forum posten kann.

Ich habe es mir mit dem Debugger angeschaut:

  • Beim ersten öffnen des Sub-Menüs werden alle RadioButtons auf false gesetzt und im Grid.DataContext die Eigenschaft Point_6_1 hinterlegt.

  • Werden nun die Einstellungen vom User getätigt und das Sub-Menü wieder geschlossen, werden alle Einstellungen gespeichert - funktioniert also.

  • Wird nun das Sub-Menü wieder geöffnet und der DataContext gesetzt und die IsChecked Eigenschaft vom RadioButton auf true gesetzt wird, springt der Code direkt wieder im Converter auf die ConvertBack Methode und übergibt nicht den eben geänderten Wert true sondern false. Somit werden alle Werte wieder auf false gesetzt.

  • Bei einer CheckBox, die sich auch auf dem Sub-Menü befindet funktioniert es, deshalb habe ich dieses Verhalten im Debugger angeschaut und dort springt der Code NICHT in die ConvertBack Methode und überschreibt wieder alle Werte.

Mir ist also nicht ganz klar wieso die ConvertBack Methode aufgerufen bzw ein falscher Wert "false" übergeben wird.

Das ist der Code für die Point_6_1 Eigenschaft:


public class Commissioning_Points_Template_YesNo
    {
        [XmlAttribute]
        public string Yes { get; set; }

        [XmlAttribute]
        public string No { get; set; }

        public static Commissioning_Points_Template_YesNo Initialize(bool isEmpty)
        {
            Commissioning_Points_Template_YesNo init;

            init = isEmpty ? new Commissioning_Points_Template_YesNo() : new Commissioning_Points_Template_YesNo()
            {
                Yes = "X",
                No = "X"
            };

            return init;
        }
    }


D
261 Beiträge seit 2015
vor 3 Jahren

@witte Gibt es noch weitere Foren für WPF? GUI: WPF und XAML erschien mir das Richtige Forum zu sein. Gib mir Bescheid, damit ich beim nächsten Mal gleich im richtigen Forum posten kann.

Es war vorher bei "GUI: Windows-Forms" und wurde dann nach "WPF und XML" verschoben.

K
Kilian Themenstarter:in
7 Beiträge seit 2019
vor 3 Jahren

Okay dann ist da etwas schief gegangen 🙁

Ich habe mal ein kleines Programm geschrieben um die Problematik besser zu erklären/vermitteln.

Es hat zwei Windows: Hautpfenster(MainWindow) und das Sub-Menü (SubMenu). Es gibt außerdem ein Converter und ein Model in dem sich die Eigenschaften befinden.

Wie am Anfang schon gesagt werden die Einstellungen der RadioButtons nicht gespeichert bzw. beim zweiten öffnen des Sub-Menüs überschrieben.

Hauptfenster:


<Window x:Class="RadioBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        WindowStartupLocation="CenterScreen"
        mc:Ignorable="d"
        Title="RadioBinding" Height="200" Width="300">
    <StackPanel Margin="10">
        <Button Content="Submenu" Margin="5" Click="CmdSubMenu_Click"/>
        <Button Content="Serialize" Margin="5" Click="CmdSerialize_Click"/>
    </StackPanel>
</Window>


using System.Windows;
namespace RadioBinding
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public Root Root = new Root();

        public MainWindow()
        {
            InitializeComponent();

            Root = Root.Initialize(true);
        }

        private void CmdSubMenu_Click(object sender, RoutedEventArgs e)
        {
            SubMenu subMenu = new SubMenu(Root);
            subMenu.ShowDialog();
        }

        private void CmdSerialize_Click(object sender, RoutedEventArgs e)
        {
            Root.Serialize("RadioBinding.xml");
        }
    }
}

Sub-Menü


<Window x:Class="RadioBinding.SubMenu"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:RadioBinding"
        mc:Ignorable="d"
        WindowStartupLocation="CenterScreen"
        Title="SubMenu" Height="200" Width="400">
    <Window.Resources>
        <local:StringConverter x:Key="StringConverter"/>
    </Window.Resources>
    <StackPanel Margin="10">
        <Grid x:Name="G_SubMenu">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            
            <Grid.Resources>
                <Style TargetType="RadioButton">
                    <Setter Property="Margin" Value="3"/>
                </Style>
            </Grid.Resources>

            <TextBlock Text="Frühstück?"/>
            <RadioButton Content="JA"
                         Grid.Column="1"
                         GroupName="Breakfast" 
                         IsChecked="{Binding MealCheck.Breakfast.Yes, Mode=TwoWay, Converter={StaticResource StringConverter}}"/>
            <RadioButton Content="NEIN"
                         Grid.Column="2"
                         GroupName="Breakfast"
                         IsChecked="{Binding MealCheck.Breakfast.No, Mode=TwoWay, Converter={StaticResource StringConverter}}"/>
            
            <TextBlock Text="Mittagessen?" Grid.Row="1"/>
            <RadioButton Content="JA" 
                         Grid.Column="1" 
                         Grid.Row="1" 
                         GroupName="Lunch"
                         IsChecked="{Binding MealCheck.Lunch.Yes, Mode=TwoWay, Converter={StaticResource StringConverter}}"/>
            <RadioButton Content="NEIN" 
                         Grid.Column="2" 
                         Grid.Row="1" 
                         GroupName="Lunch"
                         IsChecked="{Binding MealCheck.Lunch.No, Mode=TwoWay, Converter={StaticResource StringConverter}}"/>
            
            
            <TextBlock Text="Abendbrot?" Grid.Row="2"/>
            <RadioButton Content="JA" 
                         Grid.Column="1" 
                         Grid.Row="2" 
                         GroupName="Dinner"
                         IsChecked="{Binding MealCheck.Dinner.Yes, Mode=TwoWay, Converter={StaticResource StringConverter}}"/>
            <RadioButton Content="NEIN" 
                         Grid.Column="2" 
                         Grid.Row="2" 
                         GroupName="Dinner"
                         IsChecked="{Binding MealCheck.Dinner.No, Mode=TwoWay, Converter={StaticResource StringConverter}}"/>


            <TextBlock Text="Wurden alle Angaben korrekt angegeben?" Grid.Row="3"/>
            <CheckBox Content="Bestätigen"
                      Grid.Column="1" 
                      Grid.Row="3" 
                      Grid.ColumnSpan="2"  
                      Margin="3"
                      IsChecked="{Binding MealCheck.IsConfirmed, Mode=TwoWay, Converter={StaticResource StringConverter}}"/>
        </Grid>
        <Button Content="Schließen" Margin="0,5" Click="CmdClose_Click"/>
    </StackPanel>
</Window>


using System.Windows;

namespace RadioBinding
{
    /// <summary>
    /// Interaktionslogik für SubMenu.xaml
    /// </summary>
    public partial class SubMenu : Window
    {
        public SubMenu(Root root)
        {
            InitializeComponent();

            G_SubMenu.DataContext = root;
        }

        private void CmdClose_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }
    }
}

Converter:


using System;
using System.Globalization;
using System.Windows.Data;

namespace RadioBinding
{
    [ValueConversion(typeof(string), typeof(bool))]
    public class StringConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value != null && value.ToString() == "X";
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value == null ? null : ((bool)value ? "X" : null);
        }
    }
}

Model:


using System.IO;
using System.Xml.Serialization;

namespace RadioBinding
{

    public class Root
    {
        public Data MealCheck { get; set; }

        public Root Initialize(bool isEmpty)
        {
            return new Root()
            {
                MealCheck = Data.Initialize(isEmpty)
            };
        }

        public void Serialize(string fileName)
        {
            using (var fileStream = new FileStream(fileName, FileMode.Create))
            {
                XmlSerializer xml = new XmlSerializer(typeof(Root));
                xml.Serialize(fileStream, this);
            }
        }
    }

    public class Data
    {
        public Template Breakfast { get; set; }

        public Template Lunch { get; set; }

        public Template Dinner { get; set; }

        [XmlAttribute]
        public string IsConfirmed { get; set; }

        public static Data Initialize(bool isEmpty)
        {
            return new Data()
            {
                Breakfast = Template.Initialize(isEmpty),
                Lunch = Template.Initialize(isEmpty),
                Dinner = Template.Initialize(isEmpty),
                IsConfirmed = isEmpty ? "" : "X"
            };
        }
    }

    public class Template
    {
        [XmlAttribute]
        public string Yes { get; set; }

        [XmlAttribute]
        public string No { get; set; }

        public static Template Initialize(bool isEmpty)
        {
            return isEmpty ? new Template() : new Template() { Yes = "X", No = "X" };
        }
    }        
}

Xml-Ausgabe:


<?xml version="1.0"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <MealCheck IsConfirmed="X">
    <Breakfast Yes="X" />
    <Lunch Yes="X" />
    <Dinner Yes="X" />
  </MealCheck>
</Root>

K
Kilian Themenstarter:in
7 Beiträge seit 2019
vor 3 Jahren

Hallo Zusammen,

ich habe die Lösung für mein Problem nun gefunden 😃

Ich habe vergessen den DataContext vor dem schließen des Sub-Menüs auf "null" zu setzen.

Anbei die Lösung:


private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            G_SubMenu.DataContext = null;
        }

M
4 Beiträge seit 2020
vor 3 Jahren

Warum bedienst du Xml nicht wie man es soll? Man verwendet Bool wenn man bool speichern will und kein string.

5.657 Beiträge seit 2006
vor 3 Jahren

TwoWay-DataBinding funktioniert nur dann, wenn du das PropertyChanged-Ereignis auslöst. Ansonsten solltest du das Speichern und Laden von Dateien von der Benutzeroberfläche entkoppeln, das macht es wesentlich einfacher.

Siehe dazu [Artikel] MVVM und DataBinding und [Artikel] Drei-Schichten-Architektur

Weeks of programming can save you hours of planning