1

I've done a WPF base usercontrol for all my usercontrols to inherit of. In this base usercontrol class, I want to have a button associated to a click event. I've made it like this :

public class MBaseUserControl : UserControl
{
  //[...]
    protected override void OnContentChanged(object oldContent, object newContent)
    {
        base.OnContentChanged(oldContent, newContent);

        StackPanel mainPanel = new StackPanel();
        EditButton = new Button();
        EditButton.Height = EditButton.Width = 24;
        EditButton.MouseEnter += EditButton_MouseEnter;
        EditButton.MouseLeave += EditButton_MouseLeave;
        EditButton.Click += new RoutedEventHandler(EditButton_Click);
        EditButton.Background = Brushes.Transparent;
        EditButton.BorderBrush = Brushes.Transparent;
        EditButton.HorizontalAlignment = System.Windows.HorizontalAlignment.Right;
        EditButton.VerticalAlignment = System.Windows.VerticalAlignment.Top;

        StackPanel buttonPanel = new StackPanel();
        Image editButtonImage =     ImageTools.ConvertDrawingImageToWPFImage(Properties.Resources.edit, 24, 24);
        buttonPanel.Children.Add(editButtonImage);

        EditButton.Content = buttonPanel;

        mainPanel.Children.Add(EditButton);
        //Add this to new control
        ((IAddChild)newContent).AddChild(mainPanel);
        SetCMSMode(false);
    }
}

But when I click the button on GUI, nothing is firing (neither Mouse events or click event). What did I miss ?

Thanks by advance !

cdie
  • 4,014
  • 4
  • 34
  • 57
  • I would rather change the base UserControl to have the "static" controls like your edit button on a default style, that way you could bind them to commands defined in the base and not have to worry about hooking event handlers when the content changes. You could add a ContentControl to your default style and bind that to a DependencyProperty on the base. – Xtr Dec 11 '14 at 10:24
  • My WPF skills seems not be big enough to understand what you would do. Do you have an example of this ? – cdie Dec 11 '14 at 10:35
  • Maybe you should start by looking at the MVVM pattern. Plenty of resources on the internet for that, just google it. – Xtr Dec 11 '14 at 10:42

2 Answers2

1

You can try to template UserControl to have its Content shown inside some other content. Note, it's untested, but I think it should work.

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:l="clr-namespace:DemoUni">
    <ControlTemplate x:Key="SomeKey" TargetType="UserControl">
        <Grid x:Name="PART_Grid">
            <ContentPresenter x:Name="PART_Content" Content="{Binding}"/>
            <!-- ... some other content -->
        </Grid>
    </ControlTemplate>
</ResourceDictionary>

In constructor of UserControl

var dictionary = new ResourceDictionary();
// xaml containing control template
dictionary.Source = new Uri("/ProjectName;component/MyUserControlTemplate.xaml", UriKind.Relative);
Template = dictionary["SomeKey"] as ControlTemplate;

To access other content (Grid as example)

    private Grid _partGrid;
    private Grid PartGrid
    {
        get
        {
            if (_partGrid == null)
                _partGrid = (Grid)Template.FindName("PART_Grid", this);
            return _partGrid;
        }
    }

The little drawback is what you can not access PARTs in constructor, so that you have to use Loaded of UserControl to wire up events (subscribe to Loaded in constructor, subscribe to button events in Loaded).

Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • If I proceed like that, I will have to subscribe to Loaded in every UserControl I create and redefine my button behavior in every UserControl, which I don't want to do. My first goal is to define only one time in a base class the editButton behavior – cdie Dec 12 '14 at 07:54
  • Above code is exactly for base class. Maybe you misunderstood my *subscribe to `Loaded`*, that's because you [can't override](http://stackoverflow.com/a/6568375/1997232) (there is no virtual `OnLoaded`). – Sinatr Dec 12 '14 at 08:17
  • Hum, I understand what you meant. But now I implemented your code, the grid getter don't work, returns null. I don't understand why (I've not really played with template since now...) – cdie Dec 12 '14 at 09:02
  • You can not use *PART*s in constructor. You can assign template in constructor, but it is *applied* (visuals are generated?) some time later. That's why I mentioned `Loaded`, during or after this event you can access *PART*s. – Sinatr Dec 12 '14 at 09:08
  • Thanks, I put the template in loaded too, when I moved it in constructor, it works (not returning null). But my usercontrol are not displayed anymore ? I presume it's linked to ContentPresenter ? – cdie Dec 12 '14 at 09:20
  • Try `Content="{Binding Content}"` for `ContentPresenter`. – Sinatr Dec 12 '14 at 09:38
  • Not changing anything – cdie Dec 12 '14 at 09:51
  • Try `Content="{TemplateBinding Content}`. – Sinatr Dec 12 '14 at 09:52
  • Thanks, work ! You can also avoid Content attribute, it works too ! And events too – cdie Dec 12 '14 at 09:55
0

perhaps adding new MouseEventHandler to your code:

EditButton.MouseEnter += new MouseEventHandler(EditButton_MouseEnter);
EditButton.MouseLeave += new MouseEventHandler(EditButton_MouseLeave);
gjacobs
  • 133
  • 1
  • 4