简体   繁体   中英

How to bind a property in the view model to a Label.Content within a dictionary.xaml

I have an WPF MVVM app.

I have a WPF dictionary.xaml in which I have below style:

<Style x:Key="myPopupStyle" TargetType="{x:Type ContentControl}">
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Border>
                    <Grid>
                        <Path x:Name="Container"
                              Data="M8,7.41 L15.415,0 L22.83,7.41 L224,7.41 L224,130 L0,130 L0,7.41 L8,7.41"
                              Fill="White"
                              Stroke="#BEBEBE">
                        </Path>
                        <StackPanel>
                            <Label Content="{Binding Path=myText, Mode=OneWay}"/>
                            <TextBlock Margin="5,10,5,5"
                                       MaxHeight="130"
                                       MaxWidth="224"
                                       Text="{TemplateBinding Content}"
                                       TextWrapping="Wrap" />
                        </StackPanel>
                    </Grid>
                </Border>
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

Then in one of my views, I import the dictionary.xaml and I set this style on a popup:

My View (myView.xaml):

<Window x:Class="my.UI.Views.myView"
             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:vm="clr-namespace:my.UI.ViewModels"
             xmlns:v="clr-namespace:my.UI"
             mc:Ignorable="d"
             d:DesignHeight="450" d:DesignWidth="800">
    <Window.Resources>
        <ResourceDictionary>
            <!-- Dictionaries -->
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="pack://application:,,,/myApps;component/Resources/Dictionary.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Window.Resources>

            <!-- SOME STUFF -->
            <Label x:Name="StatusText" Content="Hello!"/>
            <Popup AllowsTransparency="True"
                   PlacementTarget="{Binding ElementName=StatusText}"
                   Placement="Bottom">
                <ContentControl Content="{Binding Path=myTooltip, Mode=OneWay}" Style="{StaticResource myPopupStyle}"/>
            <Popup>

            <!-- MORE STUFF --->

</Window>

My view model (myViewModel.cs) - Only showing relevant parts for this post -:

public class myViewModel : ViewModelBase
{
    public myViewModel()
    {}

    public string myText
    {
            get => this._myText;

            set
            {
                if (this._myText!= value)
                {
                    this._myText= value;
                    this.OnPropertyChanged();
                }
            }
    }

    public string myTooltip
    {
            get => this._myTooltip;

            set
            {
                if (this._myTooltip!= value)
                {
                    this._myTooltip= value;
                    this.OnPropertyChanged();
                }
            }
    }

    public string _myText;
    public string _myTooltip;
}

In my view model I have a property called myText which I am binding to the Label within the style defined in the dictionary.xaml.

When I run it, it is not working, below error is thrown:

Dispatcher processing has been suspended, but messages are still being processed.

************** Exception Text ************** System.InvalidOperationException: Dispatcher processing has been suspended, but messages are still being processed. at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled) at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o) at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs) at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)

So how can I bind the property myText which is in my view model to the Label defined within the style in the dictionary.xaml?

  1. I was trying to explain in my comment that your binding inside the Popup is wrong. The ContentControl.Content is currently bound to myViewModel.myTooltip , which is a string .
    Then the ContententControl.ContentTemplate is set to a DataTemplate that targets the wrong data type ( myViewModel ). The actual data type per binding is a string (the myTolltip value).
    In other words, the DataContext of the DataTemplate ( ContententControl.ContentTemplate ) is the object that is assigned to the ContententControl.Content property.

    To fix your issue, the ContentControl.Content must reference the myViewModel instance (the current DataContext ). You also could add another TextBlock to the DataTemplate in order to display the myTooltip value:

<Popup>
  
  <!-- Assign the myViewModel to the Content property,
       so that the type of the DataTemplate matches the Content -->
  <ContentControl Content="{Binding}" 
                  Style="{StaticResource myPopupStyle}">
    <ContentControl.ContentTemplate>
      <DataTemplate DataType="{x:Type myViewModel}">
        <Border>
          <Grid>
            <Path />

            <StackPanel>
              <TextBlock Text="{Binding myText}" />
              <TextBlock Text="{Binding myTolltip}" />
            </StackPanel>
          </Grid>
        </Border>
      </DataTemplate>
    </ContentControl.ContentTemplate>
  </ContentControl>
</Popup>
  1. BindingMode.OneWay is the default. It's redundant to set this mode explicitly.
  2. Fields should never be public . Instead you are advised to use properties. Defining the backing field of a property public would allow to bypass the property, which could lead to unexpected side effects. The only exception are event fields. They are treated specially by the compiler (eg the compiler will create a property and guards against public invocation)
  3. You should not bind a string to the Label.Content property as it results in bad performance. Since string is immutable, changing the Label.Content value (the source string ) will force the Label to reconstruct the content from scratch (eg apply a complete new template) instead of updating it.
    You should prefer a TextBlock which is optimized to handle string more efficiently.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM