简体   繁体   中英

C# WPF binding custom property

I have an application in c# WPF MVC. My objective is to create a title bar and call it for all my windows.

I created this :

XAML:

<Grid x:Class="Views.TitleBarView"
      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:Views"
      mc:Ignorable="d"
      Style="{DynamicResource TitleStyleGrid}"
      x:Name="barView">
    <Label x:Name="labelAppName" Style="{DynamicResource TitleStyleLabel}" Content="{Binding Content, ElementName=barView}"/>
    <Button x:Name="bttnClose" Style="{DynamicResource ButtonStyleCloseWindow}" Command="{Binding CloseCommand}"/>
</Grid>

c#:

public partial class TitleBarView : Grid
{
    static TitleBarView()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TitleBarView), new FrameworkPropertyMetadata(typeof(TitleBarView)));
    }

    public readonly static DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(string), typeof(TitleBarView), new PropertyMetadata(""));

    public string Content
    {
        get { return (string)GetValue(ContentProperty); }
        set { SetValue(ContentProperty, value); }
    }


    public TitleBarView()
    {
        InitializeComponent();
        TitleBarViewModel tvm = new TitleBarViewModel();
        tvm.RequestClose += (s, e) => this.Close();
        DataContext = tvm;
    }

    private void Close()
    {
            Window.GetWindow(this).Close();
    }
}

I have created the property Content for my Grid and the label inside bind this property. So when I call my class TitleBarView I just have to set property `Content``and the label automatically update.

It works good when I directly set content with String :

<Window [...]
    xmlns:local="clr-namespace:VectorReaderV3.Views"
    [...]>
    <local:TitleBarView x:Name="titleBar" Content="My Title"/>
<Window/>

But with binding, I have an empty title :

<Window [...]
    xmlns:local="clr-namespace:VectorReaderV3.Views"
    [...]>
    <local:TitleBarView x:Name="titleBar" Content="{Binding WindowTitle}">
<Window/>

What did I make wrong ?

Content="{Binding Content, ElementName=barView}"更改为Content="{Binding Content, RelativeSource={RelativeSource AncestorType=local:TitleBarView}}}"并告诉会发生什么。

I have modify the ViewModel which use my TitleBarView :

public class MessageBoxViewModel : ViewModelBase
{
    protected String windowTitle;

    public String WindowTitle {
    get { return windowTitle; }
    set { windowTitle = value; OnPropertyChanged("WindowTitle"); } }
}

And the associated View :

C#:

public partial class MessageBoxView : Window
{
    private MessageBoxView()
    {
        InitializeComponent();
        MessageBoxViewModel dvm = new MessageBoxViewModel();
        dvm.PropertyChanged += (s, e) => this.PropertyChanged(s,e);
        DataContext = dvm;
    }

    private void PropertyChanged(object e, PropertyChangedEventArgs s)
    {
        if (s.PropertyName == "WindowTitle")
        {
            titleBar.Content = (DataContext as MessageBoxViewModel).WindowTitle;
        }
    }
}

XAML:

<Window Title="MessageBoxView"
    Style="{DynamicResource WindowStyle}">

    <local:TitleBarView x:Name="titleBar" Content="{Binding WindowTitle}"/>
/>

It works but i'm not sure that it respects the pattern MVC. It's my first MVC application and I'm very confuse with it.

In your code, you set the data context of the TitleBarView to the TitleBarViewModel created in code behind. This means that the "WindowTitle" property must be on the TitleBarViewModel (which I am guessing it is not?).

You cannot make custom controls in the same way you make standard controls by inheriting from UserControl. The reason is that when creating a custom control, you intend the user to create the view model and provide the datacontext, and so you cannot create it in the code behind.

It involves a bit more work, but it boils down to this:

  • A style defines how the control looks, and it must be located in a ResourceDictionary, which is defined in/included from a Themes/Generic.xaml file (yes that specific location). The style must have a TargetType matching the name of the code-behind file.
  • The code behind file inherits from a UI class (Grid in your case), and overrides the default style with the one above - as you have done.
  • The style can use a specific TemplateBinding to bind to properties on the code behind file.
  • If the code behind needs to access named elements in the style, it must look them up using GetTemplateChild("NameOfElement") - this is often done in an override of ApplyTemplate to eg hook up button event handles.

Heres a tutorial which explains it a little more detailed: http://www.wpftutorial.net/howtocreateacustomcontrol.html

The same procedure applies if you want to modify existing controls.

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