none
Properties von Nested Controls in WPF UserControl werden bei Änderung im ViewModel nicht angepasst RRS feed

  • Frage

  • Hallo,

    Wir haben kürzlich damit begonnen WPF UserControls anzulegen (keine CustomControls!).

    Eines davon besteht aus einem Label mit einem TextBlock als Content (wegen der Möglichkeit des Zeilenumbruchs).

    Da wir diese UserControls per Drag-and-Drop von einem Toolbox-Fenster auf ein anderes WPF-Fenster ziehen, mussten wir in dem XAML-Dateien der UserControls alle x:Name-Verweise entfernen und auf ein ViewModel umsteigen.

    Das hat soweit auch ganz gut funktioniert.

    Wenn man die UserControls im XAML des ToolBox-Fensters aufnimmt, werden alle Einstellungen korrekt übernommen.

    Nun ist aber doch noch ein kleines Problem aufgetreten.

    Wenn wir ein UserControl auf dem zweiten Fenster ablegen, wird dieses nach dem Drop kopiert, der Child-Collection eines auf dem WPF-Window befindlichen Canvas hinzugefügt und als FrameworkElement an einen Bearbeitungsdialog übergeben, mit dessen Hilfe man beispielsweise den Text nachträglich anpassen kann.

    Wenn man in dem Dialog den Text ändert und ausführen möchte, wird der Setter des FrameworkElement aufgerufen und das Property umgestellt.

    Im Debugger kann man auch sehen, dass das Property im FrameworkElement den neuen Text enthält.

    Leider hat dies aber keine Auswirkung auf das Control, das auf dem WPF-Window liegt.

    Warum wird die Anzeige nicht aktualisiert?

    NotifyOnPropertyChanged ist eingebaut und funktioniert ansonsten auch.

    Dies ist das UserControl-XAML:

    <UserControl x:Class="WpfDesignerControlLibrary.ControlLabel"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                 xmlns:local="clr-namespace:WpfDesignerControlLibrary"
                 mc:Ignorable="d"
                 d:DesignHeight="30"
                 d:DesignWidth="150">
        <UserControl.Resources>
            <ResourceDictionary>
                <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="Resources.xaml"/>
                </ResourceDictionary.MergedDictionaries>
            </ResourceDictionary>
        </UserControl.Resources>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <Border Style="{StaticResource usercontrolBorderStyle}"
                    BorderBrush="{Binding Path=BorderBrush, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
                    BorderThickness="{Binding Path=BorderThickness, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
                <Label Margin="2"
                       HorizontalContentAlignment="{Binding Path=ControlHorizontalContentAlignment, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
                       VerticalContentAlignment="{Binding Path=ControlVerticalContentAlignment, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
                    <TextBlock Text="{Binding Path=Text, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
                               FontSize="{Binding Path=ControlFontSize, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
                               FontWeight="{Binding Path=ControlFontWeight, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
                               FontStyle="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=FontStyle}"
                               TextDecorations="{Binding Path=TextDecorations, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
                               TextAlignment="{Binding Path=ControlTextAlignment, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
                               TextWrapping="Wrap"/>
                </Label>
            </Border>
        </Grid>
    </UserControl>

    Dies ist ein Ausschnitt aus dem CS-File des UserControls:

        public partial class ControlLabel : UserControl
        {
            /// <summary>
            /// Field for an instance of the <see cref="ControlWithTextBlockViewModel"/> class.
            /// </summary>
            ControlWithTextBlockViewModel viewmodel = new ControlWithTextBlockViewModel();
    
            /// <summary>
            /// The constructor.
            /// </summary>
            public ControlLabel()
            {
                InitializeComponent();
                viewmodel.ControlFontSize = base.FontSize;
                this.DataContext = viewmodel;
            }
    
            /// <summary>
            /// Gets or sets the text/input of the control.
            /// </summary>
            public string Text
            {
                get { return viewmodel.Text; }
                set { viewmodel.Text = value; }
            }

    Und hier noch das Property aus dem ViewModel:

            public string Text
            {
                get { return text; }
                set
                {
                    text = value;
                    RaisePropertyChanged("Text");
                }
            }

    Nach dem Drop auf dem zweiten WPF-Window wird das UserControl per XamlSerialisierung zu einem String verarbeitet und danach als FrameworkElement deserialisiert. Der String von der Serialisierung sieht so aus:

    <ControlLabel Text="Label1" ControlFontSize="12" ControlFontWeight="Normal" ControlFontStyle="Normal" ControlHorizontalContentAlignment="Left" ControlVerticalContentAlignment="Top" BorderBrush="#FF00FFFF" BorderThickness="1,1,1,1" av:Canvas.Left="103" av:Canvas.Top="214" xmlns="clr-namespace:WpfDesignerControlLibrary;assembly=WpfDesignerControlLibrary" xmlns:assembly="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:av="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
      <ControlLabel.TextDecorations>
        <assembly:Null />
      </ControlLabel.TextDecorations>
      <av:Grid>
        <av:Grid.ColumnDefinitions>
          <av:ColumnDefinition />
        </av:Grid.ColumnDefinitions>
        <av:Grid.RowDefinitions>
          <av:RowDefinition Height="*" />
        </av:Grid.RowDefinitions>
        <av:Border BorderThickness="0,0,0,0" BorderBrush="{assembly:Null}">
          <av:Border.Style>
            <av:Style TargetType="av:IFrameworkInputElement">
              <av:Style.Resources>
                <av:ResourceDictionary />
              </av:Style.Resources>
              <av:Setter Property="av:Border.BorderBrush">
                <av:Setter.Value>
                  <av:SolidColorBrush>#FF4169E1</av:SolidColorBrush>
                </av:Setter.Value>
              </av:Setter>
              <av:Setter Property="av:Border.BorderThickness">
                <av:Setter.Value>
                  <av:Thickness>1,1,1,1</av:Thickness>
                </av:Setter.Value>
              </av:Setter>
              <av:Setter Property="av:FrameworkElement.HorizontalAlignment">
                <av:Setter.Value>
                  <assembly:Static Member="av:HorizontalAlignment.Stretch" />
                </av:Setter.Value>
              </av:Setter>
              <av:Setter Property="av:FrameworkElement.VerticalAlignment">
                <av:Setter.Value>
                  <assembly:Static Member="av:VerticalAlignment.Stretch" />
                </av:Setter.Value>
              </av:Setter>
            </av:Style>
          </av:Border.Style>
          <av:Label HorizontalContentAlignment="Left" VerticalContentAlignment="Top" Margin="2,2,2,2">
            <av:TextBlock Text="Label1" FontStyle="Normal" FontWeight="Normal" FontSize="12" TextAlignment="Left" TextWrapping="Wrap">
              <av:TextBlock.TextDecorations>
                <assembly:Null />
              </av:TextBlock.TextDecorations>
            </av:TextBlock>
          </av:Label>
        </av:Border>
      </av:Grid>
    </ControlLabel>

    Montag, 2. Mai 2016 13:16

Antworten

  • Das Problem besteht allem Anschein nach darin, dass die XAML-Serialisierung die Bindings zumindest nach außen entfernt.

    Intern werden zwar die Setter der ViewModel-Properties aufgerufen, aber nach außen werden die aktuellen Werte nicht mehr weitergegeben.

    In diesem Fall muss man von dem gezogenen UserControl eine neue Instanz erzeugen, anstatt es per XAML-Serialisierung und -Deserialisierung zu kopieren.


    Oder gibt es doch noch eine andere Möglichkeit, die ich übersehen habe?
    Dienstag, 3. Mai 2016 07:26