0

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 ?

AnjumSKhan
  • 9,647
  • 1
  • 26
  • 38
A.Pissicat
  • 3,023
  • 4
  • 38
  • 93
  • Where does this `Content="{Binding WindowTitle}"` come from? – lokusking Jul 04 '16 at 10:57
  • 3
    This is the standard example of why you should never explicitly set the DataContext of a custom control (as you do in the TitleBarView constructor). Doing so effectively prevents inheriting a DataContext, which you are implicitly expecting when writing `Content="{Binding WindowTitle}"`. – Clemens Jul 04 '16 at 11:23
  • I've learn to set the DataContext like this, what is the best way ? – A.Pissicat Jul 04 '16 at 11:56
  • See here: http://stackoverflow.com/a/25673948/1136211 – Clemens Jul 04 '16 at 12:00

3 Answers3

0

Change Content="{Binding Content, ElementName=barView}" to Content="{Binding Content, RelativeSource={RelativeSource AncestorType=local:TitleBarView}}}" and tell what happens.

AnjumSKhan
  • 9,647
  • 1
  • 26
  • 38
0

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.

A.Pissicat
  • 3,023
  • 4
  • 38
  • 93
0

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 e.g. 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.

sondergard
  • 3,184
  • 1
  • 16
  • 25