4

I have created a wpf application into which I have added a user control and a custom control. The custom control is used within the user control (planned to be used in many other Types of user controls).

For a quick example, the custom control has a dependency property of type Brush titled backgroundcolour, This is then set in the defualt template for the custom control as the background for the border. My thinking was that I would be able to set this value from the user control using the Command and CommandParameter properties. As i tried to do below

User Control xaml (TestControl is the custom control)

<Grid>
    <Grid.Resources>
        <MyNamespace:EditColourCommand x:Key="EditColour"/>
    </Grid.Resources>

    <Grid.ContextMenu>
        <ContextMenu>
            <MenuItem Name="Test" 
                      Header="Test" 
                      Command="{StaticResource EditColour}" 
                      CommandParameter="{Binding ElementName=testControl1, Path=BackgroundColour, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        </ContextMenu>
    </Grid.ContextMenu>

    <MyNamespace:TestControl HorizontalAlignment="Left" 
                             Margin="213,90,0,0" 
                             x:Name="testControl1" 
                             VerticalAlignment="Top" 
                             Height="77" 
                             Width="230"/>

</Grid>

My custom Control Code Behind:

public class TestControl : Control
{
    static TestControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(TestControl), 
            new FrameworkPropertyMetadata(typeof(TestControl)));
    }

    public static DependencyProperty BackgroundColourProperty = 
        DependencyProperty.Register("BackgroundColour", 
                                    typeof(Brush), 
                                    typeof(TestControl), 
                                    new PropertyMetadata(Brushes.Blue, 
                                        BackgroundColourPropertyChangedHandler));

    public Brush BackgroundColour
    {
        get
        {
            return (Brush)GetValue(BackgroundColourProperty);
        }
        set
        {
            SetValue(BackgroundColourProperty, value);
        }
    }

    public static void BackgroundColourPropertyChangedHandler(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
    } 
}

Finally, The Command

public class EditColourCommand : ICommand
{
    public bool CanExecute(object parameter)
    {
        return true;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        parameter = Brushes.Black;
    }
}

The command gets fired and it has the defualt value in this case blue as a parameter but it never sets the value to black. Can someone push me in the right direction please?

Benjamin Gale
  • 12,977
  • 6
  • 62
  • 100
E.L Dunn
  • 551
  • 6
  • 20
  • Similar question: http://stackoverflow.com/questions/2985547/xaml-namescope-and-template-question-concerning-wpf-contextmenu – Luke Woodward Jun 21 '12 at 21:33
  • Although they are dealing with the same sort of things, menuitems and binding. He, from what i can gather is trying to have mutliple menu items depending on style. i on the other hand am trying to set a dependency property held by the custom control that could be set on any object in the custom control template. so that someone can re-template the control and still get the same effect if they bind to the dependency property. as dowhilefor showed below i can send the actual object as a parementer and change it that way so there are no scoping issues. i was just hoping for a general command. – E.L Dunn Jun 22 '12 at 07:33
  • Another similar question: http://stackoverflow.com/questions/1013558/elementname-binding-from-menuitem-in-contextmenu – Luke Woodward Jun 22 '12 at 20:19

2 Answers2

1

Ok After several days of searching i have something that i am happy with. I thought i would just add it here incase anyone else has a similar requirement. The main issue i was facing was the total unawareness (if thats a word) of the "routedcommand", i knew of commands and routedevents but not the routedcommand type. I also dropped the idea of a custom control inside a usercontrol although im sure i could re-add that if i needed too. Anyways heres the code i am now using.

public class Block : Control
{
    static Block()

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        CommandBindings.Add(new CommandBinding(EditColorCommand, EditColor, CanEditColor));  
    }

    public static readonly DependencyProperty TitleBackgroundColorProperty = DependencyProperty.Register("TitleBackgroundColor", typeof(SolidColorBrush), typeof(Block), new FrameworkPropertyMetadata(Brushes.Gray, null));
    public SolidColorBrush TitleBackgroundColor
    {
        get { return (SolidColorBrush)base.GetValue(TitleBackgroundColorProperty); }
        set { base.SetValue(TitleBackgroundColorProperty, value); }
    }

    public static readonly DependencyProperty TitleLeftTextColorProperty = DependencyProperty.Register("TitleLeftTextColor", typeof(SolidColorBrush), typeof(Block), new FrameworkPropertyMetadata(Brushes.Black, null));
    public SolidColorBrush TitleLeftTextColor
    {
        get { return (SolidColorBrush)base.GetValue(TitleLeftTextColorProperty); }
        set { base.SetValue(TitleLeftTextColorProperty, value); }
    }

    public static readonly DependencyProperty TitleRightTextColorProperty = DependencyProperty.Register("TitleRightTextColor", typeof(SolidColorBrush), typeof(Block), new FrameworkPropertyMetadata(Brushes.Black, null));
    public SolidColorBrush TitleRightTextColor
    {
        get { return (SolidColorBrush)base.GetValue(TitleRightTextColorProperty); }
        set { base.SetValue(TitleRightTextColorProperty, value); }
    }


    public static readonly DependencyProperty BodyBackgroundLowColorProperty = DependencyProperty.Register("BodyBackgroundLowColor", typeof(SolidColorBrush), typeof(Block), new FrameworkPropertyMetadata(Brushes.Gray, null));
    public SolidColorBrush BodyBackgroundLowColor
    {
        get { return (SolidColorBrush)base.GetValue(BodyBackgroundLowColorProperty); }
        set { base.SetValue(BodyBackgroundLowColorProperty, value); }
    }
    public static readonly DependencyProperty BodyBackgroundHighColorProperty = DependencyProperty.Register("BodyBackgroundHighColor", typeof(SolidColorBrush), typeof(Block), new FrameworkPropertyMetadata(Brushes.Gray, null));
    public SolidColorBrush BodyBackgroundHighColor
    {
        get { return (SolidColorBrush)base.GetValue(BodyBackgroundHighColorProperty); }
        set { base.SetValue(BodyBackgroundHighColorProperty, value); }
    }

    public static readonly ICommand EditColorCommand = new RoutedCommand("EditColor", typeof(Block));
    public static void EditColor(Object sender, ExecutedRoutedEventArgs e)
    {
        DependencyProperty dp = e.Parameter as DependencyProperty;
        SolidColorBrush dpVal = ((Block)sender).GetValue(dp) as SolidColorBrush;

        ((Block)sender).SetValue(dp, new SolidColorBrush(Colors.Blue));
    }
    public static void CanEditColor(Object sender, CanExecuteRoutedEventArgs e)
    {
        DependencyProperty dp = e.Parameter as DependencyProperty;

        if (((Block)sender).GetValue(dp).GetType() == typeof(SolidColorBrush))
            e.CanExecute = true;
        else
            e.CanExecute = false;
    }
}

The Defualt Template

<Style TargetType="{x:Type local:Block}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:Block}">
                <Border BorderBrush="Black" BorderThickness="1" CornerRadius="3,3,6,6">
                    <Border.Background>
                        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                            <GradientStop Color="{Binding BodyBackgroundHighColor.Color,  RelativeSource={RelativeSource TemplatedParent}}" Offset="0" />
                            <GradientStop Color="{Binding BodyBackgroundLowColor.Color,  RelativeSource={RelativeSource TemplatedParent}}" Offset="1" />
                        </LinearGradientBrush>
                    </Border.Background>
                    <Grid Name="MainGrid" Background="Transparent">
                        <Grid.Resources>
                            <ContextMenu x:Key="TitleContextMenuKey">
                                <MenuItem Name="EditTitleBackgroundColorCommand" Header="Edit Title Background Color" CommandParameter="{x:Static local:Block.TitleBackgroundColorProperty}" Command="{x:Static local:Block.EditColorCommand}" CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource FindAncestor,  AncestorType={x:Type ContextMenu}}}" />
                                <MenuItem Name="EditTitleTextLeftColorCommand" Header="Edit Left Title Text Color" CommandParameter="{x:Static local:Block.TitleLeftTextColorProperty}" Command="{x:Static local:Block.EditColorCommand}" CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource FindAncestor,  AncestorType={x:Type ContextMenu}}}" />
                                <MenuItem Name="EditTitleTextRightColorCommand" Header="Edit Right Title Text Color" CommandParameter="{x:Static local:Block.TitleRightTextColorProperty}" Command="{x:Static local:Block.EditColorCommand}" CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource FindAncestor,  AncestorType={x:Type ContextMenu}}}" />
                            </ContextMenu>
                            <ContextMenu x:Key="BodyContextMenuKey">
                                <MenuItem Name="EditBodyBackgroundLowColorCommand" Header="Edit Body Low Background Color" CommandParameter="{x:Static local:Block.BodyBackgroundLowColorProperty}" Command="{x:Static local:Block.EditColorCommand}" CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource FindAncestor,  AncestorType={x:Type ContextMenu}}}" />
                                <MenuItem Name="EditBodyBackgroundHighColorCommand" Header="Edit Body High Background Color" CommandParameter="{x:Static local:Block.BodyBackgroundHighColorProperty}" Command="{x:Static local:Block.EditColorCommand}" CommandTarget="{Binding Path=PlacementTarget, RelativeSource={RelativeSource FindAncestor,  AncestorType={x:Type ContextMenu}}}" />
                            </ContextMenu>
                        </Grid.Resources>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="30" />
                            <RowDefinition Height="*" MinHeight="50" />
                        </Grid.RowDefinitions>
                        <Viewbox Name="TitleBar" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Stretch="Fill" >
                            <Viewbox.Style>
                                <Style>
                                    <Setter Property="Viewbox.ContextMenu" Value="{StaticResource TitleContextMenuKey}"/>
                                </Style>
                            </Viewbox.Style>
                            <Canvas Name="TitleBarDesign" Width="750" Height="200">
                                <Canvas.Resources>
                                    <LinearGradientBrush xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Key="linearGradient3932" MappingMode="Absolute" StartPoint="376,-69" EndPoint="384,131">
                                        <LinearGradientBrush.GradientStops>
                                            <GradientStopCollection>
                                                <GradientStop Color="#FFFFFFFF" Offset="0"/>
                                                <GradientStop Color="#00FFFFFF" Offset="1"/>
                                            </GradientStopCollection>
                                        </LinearGradientBrush.GradientStops>
                                        <LinearGradientBrush.Transform>
                                            <TranslateTransform X="-0.0625" Y="2.3125"/>
                                        </LinearGradientBrush.Transform>
                                    </LinearGradientBrush>
                                    <LinearGradientBrush xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Key="linearGradient3905" MappingMode="Absolute" StartPoint="372,293" EndPoint="366,111">
                                        <LinearGradientBrush.GradientStops>
                                            <GradientStopCollection>
                                                <GradientStop Color="#FF000000" Offset="0"/>
                                                <GradientStop Color="#00000000" Offset="1"/>
                                            </GradientStopCollection>
                                        </LinearGradientBrush.GradientStops>
                                        <LinearGradientBrush.Transform>
                                            <TranslateTransform X="-0.06249985" Y="2.3124958"/>
                                        </LinearGradientBrush.Transform>
                                    </LinearGradientBrush>
                                </Canvas.Resources>
                                <Canvas Name="TitleBarImagery">
                                    <Canvas.RenderTransform>
                                        <TranslateTransform X="0.0625" Y="-2.3125"/>
                                    </Canvas.RenderTransform>
                                    <Rectangle Name="Color" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="750" Height="200" RadiusX="9.8214264" RadiusY="9.8214264" Fill="{TemplateBinding TitleBackgroundColor}" />
                                    <Rectangle Name="Shadow" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Canvas.Left="0" Canvas.Top="2" Width="750" Height="199" RadiusX="9.8214264" RadiusY="9.8214264" Fill="{StaticResource linearGradient3905}" StrokeThickness="0" Stroke="#FF000000" StrokeMiterLimit="4" StrokeLineJoin="Round" StrokeStartLineCap="Flat" StrokeEndLineCap="Flat"/>
                                    <Path Name="Highlight" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Fill="{StaticResource linearGradient3932}" StrokeThickness="0" Stroke="#FF000000" StrokeMiterLimit="4" StrokeLineJoin="Round" StrokeStartLineCap="Flat" StrokeEndLineCap="Flat">
                                        <Path.Data>
                                            <PathGeometry Figures="m 14.08947 7.4627848 c -5.6134084 0 -10.1232994 6.7033202 -10.1232994 14.9876462 l 0 0.666118 C 86.399783 75.974267 227.79549 110.85376 388.42587 110.85376 c 143.91412 0 272.39283 -28.007413 356.86245 -71.845549 l 0 -16.55778 c 0 -8.284326 -4.50989 -14.9876462 -10.12331 -14.9876462 l -721.07554 0 z" FillRule="NonZero"/>
                                        </Path.Data>
                                    </Path>
                                </Canvas>
                            </Canvas>
                        </Viewbox>
                        <Label Name="lblLeftText" Content="{TemplateBinding TitleLeftText}" Foreground="{TemplateBinding TitleLeftTextColor}" HorizontalAlignment="Left" />
                        <Label Name="lblRightText" Content="{TemplateBinding TitleRightText}" Foreground="{TemplateBinding TitleRightTextColor}" HorizontalAlignment="Right"/>
                        <StackPanel Name="Body"  Grid.Row="1" Background="Transparent">
                            <StackPanel.Style>
                                <Style>
                                    <Setter Property="StackPanel.ContextMenu" Value="{StaticResource BodyContextMenuKey}"/>
                                </style>
                        </StackPanel>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Hopefully u can see that the routedcommand is used to change several different dependency poperties determined by the CommandParameter. This is probably obvious to alot of people, but i had a real pain hooking this up (probably me being dumb). maybe it will help someone if not no harm done.

E.L Dunn
  • 551
  • 6
  • 20
0

Just modify your Command to

CommandParameter="{Binding ElementName=testControl1}"

and modify your command execute to

public void Execute(object parameter)
{
    var ctrl = parameter as TestControl 
    ctrl.BackgroundColour = Brushes.Black;
}

you can't expect the binding to work like you did in the CommandParameter.

dowhilefor
  • 10,971
  • 3
  • 28
  • 45
  • Thanks for the answer, is there no way to pass in a parameter of type brush so the command is alot more general (so that any property expecting a value of brush could be passed in)?? – E.L Dunn Jun 21 '12 at 20:24
  • well doesnt look like im going to get anymore indepth answers and this one does work although not in the way i hoped, so ill mark it as accepted. thanks – E.L Dunn Jun 24 '12 at 23:16
  • @E.LDunn Sorry, was to busy the last days. The only other way to not now about the control itself, would be to pass in your ViewModel, which has the background property and set that. On the viewmodel the property is bound back to the ui. Having an Interface for the Background property also makes this command reusable. – dowhilefor Jun 25 '12 at 09:02