I am trying to access commands that are defined in MainWindow.xaml in another window. I am only able to get grayed out titles of these commands. I am wondering what should be should be done in order to get a full access.
Sample of the command:
public partial class MainWindow : Window
{
public static RoutedUICommand AddCommand1 = new RoutedUICommand("Command ", "command1", typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
this.CommandBindings.Add(new CommandBinding(AddCommand1, AddCommand1Executed));
}
private void AddCommand1Executed(object sender, ExecutedRoutedEventArgs e)
{
AddNewItem picker = new AddNewItem();
picker.ShowDialog();
}
I access these command in style through databinding:
<Menu x:Name="TaskMenuContainer"><MenuItem x:Name="menuItem" Header="TASKS" ItemsSource="{Binding}" Template="{DynamicResource TaskMenuTopLevelHeaderTemplateKey}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding}" />
<Setter Property="Header" Value="{Binding Text, RelativeSource={RelativeSource Self}}" />
<Setter Property="CommandTarget" Value="{Binding RelativeSource={RelativeSource Self}}"/>
</Style>
</MenuItem.ItemContainerStyle>
These commands work in pages that is loaded inside MainWindow.xaml through frame. However, if I have pop up window that is not part of MainWindow.xaml these commands are only grayed out and not functional anymore (cannot be executed). Any advice is highly appreciated!
The way you define the command, you define it for a particular window. If you want to handle the command globally, at the application level, you can use CommandManager.RegisterClassCommandBinding:
First, define you command in a separate static class:
public static class GlobalCommands
{
public static RoutedUICommand AddCommand1 = new RoutedUICommand("Command ", "command1", typeof(MainWindow));
}
Then, in you window or whatever place you want to put the command logic, register the command handlers:
public partial class MainWindow : Window
{
static MainWindow()
{
CommandManager.RegisterClassCommandBinding(typeof(Window), new CommandBinding(GlobalCommands.AddCommand1, AddCommand1Executed, CanAddExecute));
}
private static void AddCommand1Executed(object sender, ExecutedRoutedEventArgs e)
{
AddNewItem picker = new AddNewItem();
picker.ShowDialog();
}
}
And in your menu style you should change the binding to x:Static:
<Setter Property="Command" Value="{x:Static my:GlobalCommands.AddCommand1}" />
When the command is routed, when checking for command bindings in each active element in the UI, the bindings registered for each element's class will also be checked. By registering the binding here, you can cause every instance of a class to be able to handle the specific command.
So, in the above example, the type Window is used and this will cause the routing to find the command binding in any instance of Window, once the routing reaches that instance in its search for a command binding.
You could instead, for example, restrict the handling to a specific subclass of Window, so that the command will only be bound in an instance of that subclass. Or you can use some other UI element type, so that that the presence of that specific type of element will cause the event to be handled. Just set the owning type for the registered command binding appropriately for your specific needs.
Related
I am using visual studio dark theme. As a result when designing my views I cannot see the font if its black. A fix will be to set the background of the view to white. But our application has different themes so I cannot hard code that.
There are to great properties that I use when creating an usercontrol:
d:DesignWidth="1110" d:DesignHeight="400"
those properties are only affecting the view at design time. It will be great if I can create a property d:DesignBackground just so that I do not have to be adding and removing the background property every time I run the application.
Not sure if it's exactly what you're looking for, but what I do is just plop a trigger in the app.xaml to invoke using the IsInDesignMode property like;
Namespace (Thanks Tono Nam);
xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=PresentationFramework"
XAML;
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<Trigger Property="ComponentModel:DesignerProperties.IsInDesignMode"
Value="True">
<Setter Property="Background"
Value="#FFFFFF" />
</Trigger>
</Style.Triggers>
</Style>
Simple, but works, and sometimes I target other dependency properties like font and stuff too depending on the need. Hope this helps.
PS - You can target other TargetType's with their own properties the same way, like for example, ChildWindows, Popups, Windows, whatever...
You can create a static class with an attached property for design mode:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Helpers.Wpf
{
public static class DesignModeHelper
{
private static bool? inDesignMode;
public static readonly DependencyProperty BackgroundProperty = DependencyProperty
.RegisterAttached("Background", typeof (Brush), typeof (DesignModeHelper), new PropertyMetadata(BackgroundChanged));
private static bool InDesignMode
{
get
{
if (inDesignMode == null)
{
var prop = DesignerProperties.IsInDesignModeProperty;
inDesignMode = (bool) DependencyPropertyDescriptor
.FromProperty(prop, typeof (FrameworkElement))
.Metadata.DefaultValue;
if (!inDesignMode.GetValueOrDefault(false) && Process.GetCurrentProcess().ProcessName.StartsWith("devenv", StringComparison.Ordinal))
inDesignMode = true;
}
return inDesignMode.GetValueOrDefault(false);
}
}
public static Brush GetBackground(DependencyObject dependencyObject)
{
return (Brush) dependencyObject.GetValue(BackgroundProperty);
}
public static void SetBackground(DependencyObject dependencyObject, Brush value)
{
dependencyObject.SetValue(BackgroundProperty, value);
}
private static void BackgroundChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!InDesignMode)
return;
d.SetValue(Control.BackgroundProperty, e.NewValue);
}
}
}
And you can use it like this:
xmlns:wpf="clr-namespace:Helpers.Wpf;assembly=Helpers.Wpf"
<Grid Background="Black"
wpf:DesignModeHelper.Background="White">
<Button Content="Press me!"/>
</Grid>
You can use this approach to implement other property for design mode.
In 2022 you actually can and almost guessed it:
d:Background="White"
Note you can set pretty much any other control properties differently for design-time when prefix them with d:, for example:
<TextBlock Text="{Binding TextFromViewModel}"
Foreground="{StaticResource PrimaryBrush}"
d:Text="Design-time text"
d:Foreground="Black" />
Source: Use Design Time Data with the XAML Designer in Visual Studio
Usual WPF architecture:
public partial class MainWindow: Window {
... InitializeComponent()
}
XAML: <Window x:Class="MainWindow"> </Window>
What I want to move to:
public abstract class BaseWindow: Window {
public System.Windows.Controls.TextBlock control1;
public System.Windows.Shapes.Rectangle control2;
public System.Windows.Controls.TextBox control3;
}
public partial class AWindowImplementation {
... InitializeComponent()
}
public partial class AnotherWindowImplementation{
... InitializeComponent()
}
XAML:
<BaseWindow x:Class="AWindowImplementation"> </BaseWindow>
<BaseWindow x:Class="AnotherWindowImplementation"> </BaseWindow>
The above is pseudo-code. This new architecture compiles, with warnings that the implementations hide the control defintions (because the place where I should put the 'override' keywords are withing the auto-generated InitializeComponent). Unfortunately the control fields don't get populated.
Is this achievable? What I am trying to do is create several UI designs with the same interface/controls so that the rest of the code can interact with either design.
EDIT: Thanks to pchajer and Yevgeniy, I now have the following working solution, but I still get override warnings:
public class MainWindowBase : Window
{
public TextBlock control1;
public Rectangle control2;
public TextBox control3;
static MainWindowBase()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MainWindowBase),
new FrameworkPropertyMetadata(typeof(MainWindowBase)));
}
public override void OnApplyTemplate()
{
control1 = (TextBlock) FindName("control1");
control2 = (Rectangle) FindName("control2");
control3 = (TextBox) FindName("control3");
}
}
<Style TargetType="{x:Type views:MainWindowBase}"
BasedOn="{StaticResource {x:Type Window}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type views:MainWindowBase}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
public partial class AWindowImplementation :MainWindowBase {
... InitializeComponent()
}
<MainWindowBase x:Class="AWindowImplementation"> </MainWindowBase>
I guess I will have to use different field names in the base class to get rid of the warnings, or perhaps remove InitializeComponent in the derived class. But anyway it works now.
In WPF you can create a Base class which inherits from Window and that has a XAML. But there is a workground
refer this link - How to create a common WPF base window style?
I'm not sure how you would expect that pseudo-code to work as nothing is calling your InitializeComponent. Ordinarily, WPF calls it on your behalf in your window's constructor. But in your case you're adding a new implementation (not an override) and nothing is calling it.
One option is to just call your new implementation from each subclass constructor. eg. AWindowImplementation's constructor could call this.InitializeComponent().
Another option is for BaseWindow to define a virtual method (say, InitializeComponentCore) that its constructor calls. Base classes can then override that method.
You need to define base class for Window as a custom control. Just create a new custom control, set base class to Window and then insert style from blend (you may add yours components). See also answer from pchajer.
In WPF XAML there is the convenient DesignHeight and DesignWidth, for instance in code as
<UserControl ... d:DesignHeight="500" d:DesignWidth="500" ... />
which is great because I can build the layout with a representative, but not locked-in, control size.
However, I'm often building dark UIs, where labels and so forth need to be white, but my controls still need a transparent background color. This creates a design-time inconvenience because white seems to be the default background color for transparent controls in the designer, leading to unreadable white-on-white labels.
Is there a way or strategy for setting the design-time background color, with similar convenience as DesignHeight/DesignWidth?
There's an undocumented property d:DesignStyle of type Style that you can set on a user control. This style is only applied in the designer and is not used at runtime.
You use it like this:
<UserControl ... d:DesignStyle="{StaticResource MyDesignStyle}" />
Or like this:
<UserControl ...>
<d:DesignerProperties.DesignStyle>
<Style TargetType="UserControl">
<Setter Property="Background" Value="White" />
<Setter Property="Height" Value="500" />
<Setter Property="Width" Value="500" />
</Style>
</d:DesignerProperties.DesignStyle>
</UserControl>
The Background property is what you asked for. The Height and Width do replace your d:DesignHeight=500 and d:DesignWidth=500 in the <UserControl> tag. Then you have all your design properties at one place.
Note however, that any value set on the Style property (the one used at runtime) will also override the DesignStyle in the designer.
I found that you can do one for yourself. Custom design-time attributes in Silverlight and WPF designer is a tutorial how to do it for both Silverlight and WPF.
My answer was found here: Black Background for XAML Editor. There are a number of choices including checking System.ComponentModel.DesignerProperties.GetIsInDesignMode(this) at runtime.
This is the complete solution for DesignBackground:
public class DesignTimeProperties : DependencyObject
{
private static readonly Type OwnerType = typeof(DesignTimeProperties);
#region DesignBackground (attached property)
public static Brush GetDesignBackground(DependencyObject obj)
{
return (Brush)obj.GetValue(DesignBackgroundProperty);
}
public static void SetDesignBackground(DependencyObject obj, Brush value)
{
obj.SetValue(DesignBackgroundProperty, value);
}
public static readonly DependencyProperty DesignBackgroundProperty =
DependencyProperty.RegisterAttached(
"DesignBackground",
typeof (Brush),
OwnerType,
new FrameworkPropertyMetadata(Brushes.Transparent,
DesignBackgroundChangedCallback));
public static void DesignBackgroundChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (IsInDesignMode)
{
var control = d as Control;
var brush = e.NewValue as Brush;
if (control != null && brush != null)
{
control.Background = brush;
}
}
}
public static bool IsInDesignMode
{
get
{
return
((bool)
DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof (DependencyObject)).DefaultValue);
}
}
#endregion
}
Usage:
<UserControl ... infra:DesignTimeProperties.DesignBackground="Black" />
The d:DesignerProperties.DesignStyle technique shown on this page works great for applying a WPF design-time-only style to a single control, but it doesn't appear to work for a Style in a ResourceDictionary that would apply to all of the appropriately-typed controls or elements under the scope of the dictionary. Below is simple solution I found for deploying a designer-only style into a ResourceDictionary.
Consider for example a Window containing a TreeView, where we want the TreeViewItem nodes to show as fully expanded—but only at design time. First, put the desired style in the XAML dictionary in the normal way.
<Window.Resources>
<Style TargetType="TreeViewItem">
<Setter Property="IsExpanded" Value="True" />
</Style>
</Window.Resources>
Here, the Style is put in the ResourceDictionary of the Window but of course you could use any other subsuming dictionary instead. Next, in the C# code, remove the style from the ResourceDictionary when design mode is not detected. Do this is in the OnInitialized override:
protected override void OnInitialized(EventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(this) == false)
Resources.Remove(typeof(TreeViewItem));
base.OnInitialized(e);
}
Design Mode: Runtime Mode:
I have a ClockFace UserControl that exposes a number of properties to enable users to style it. The clock has two Ellipse objects as borders; an outer border and an inner border.
<Ellipse Name="OuterBorder" Panel.ZIndex="5" StrokeThickness="{Binding BorderOuterThickness}" Stroke="{Binding BorderOuteBrush}" />
<Ellipse Name="InnerBorder" Panel.ZIndex="6" StrokeThickness="{Binding BorderInnerThickness}" Margin="{Binding StrokeThickness, ElementName=OuterBorder}" Stroke="{Binding BorderInnerBrush}">
public static readonly DependencyProperty BorderInnerBrushProperty = DependencyProperty.Register("BorderInnerBrush", typeof(Brush), typeof(ClockFace), new
PropertyMetadata(new LinearGradientBrush(Color.FromRgb(118, 57, 57), Color.FromRgb(226, 185, 185), new Point(0.5, 0), new Point(0.5, 1))));
public Brush BorderInnerBrush
{
get { return (Brush)GetValue(BorderInnerBrushProperty); }
set { SetValue(BorderInnerBrushProperty, value); }
}
public static readonly DependencyProperty BorderOuterBrushProperty = DependencyProperty.Register("BorderOuterBrush", typeof(Brush), typeof(ClockFace), new
PropertyMetadata(new LinearGradientBrush(Color.FromRgb(226, 185, 185), Color.FromRgb(118, 57, 57), new Point(0.5, 0), new Point(0.5, 1))));
public Brush BorderOuterBrush
{
get { return (Brush)GetValue(BorderOuterBrushProperty); }
set { SetValue(BorderOuterBrushProperty, value); }
}
These can be set in a style and will update correctly when the styles are switched. I thought that I would be clever and add a shortcut property called BorderBrush that passes itself to the BorderOuterBrush property and then passes a copy of itself with its gradient reversed to the BorderInnerBrush property. To enable this code to run when the property was set after initialisation (by switching styles), I had to implement a PropertyChangedCallback method that calls the SetBorderBrushes method.
public new static readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(ClockFace), new
PropertyMetadata(OnBorderBrushChanged));
private static void OnBorderBrushChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
((ClockFace)dependencyObject).SetBorderBrushes((Brush)e.NewValue);
}
public new Brush BorderBrush
{
get { return (Brush)GetValue(BorderBrushProperty); }
set { SetValue(BorderBrushProperty, value); }
}
private void SetBorderBrushes(Brush brush)
{
if (brush != null)
{
BorderOuterBrush = brush;
Brush innerBrush = BorderOuterBrush.Clone();
if (brush.GetType() == typeof(LinearGradientBrush) || brush.GetType() == typeof(RadialGradientBrush))
{
foreach (GradientStop gradientStop in ((GradientBrush)innerBrush).GradientStops)
{
gradientStop.Offset = 1 - gradientStop.Offset;
}
}
BorderInnerBrush = innerBrush;
}
}
When the BorderBrush property is set in a style, it all works just fine... that is until I switch the style at run time. Now the strangest thing happens... let me explain.
I have four preset styles and individually, they all work just fine. Three of them use the two BorderInnerBrush and BorderOuterBrush properties and one uses the shortcut BorderBrush property. I can switch between the styles using a ContextMenu and some code behind that accesses the xaml styles from Resources and sets them to the ClockFace object's Style property.
I can switch between the three styles that don't use the shortcut property endlessly without problem. I can also switch to the style that does use the shortcut property and it appears just fine. This is when the strangeness begins.
After switching to the style that uses the BorderBrush property, the BorderInnerBrush and BorderOuterBrush properties simply stop working. The preset Brush objects set in the various styles no longer get set on the two Ellipse objects. I pluged in some PropertyChangedCallback methods to the inner and outer border properties to see what was going on.
When I first run the application, I can switch to the three styles that don't use the shortcut property without problem. I put breakpoints on all three border Brush properties' PropertyChangedCallback methods and debugged the program. When switching to each of these three styles, the inner and outer border properties' callback methods' breakpoints were hit as you would expect. When switching to the style that uses the BorderBrush property, its callback method's breakpoint was hit. The SetBorderBrushes method sets the two other border Brush objects so the inner and outer border properties' callback methods' breakpoints were then hit as you would expect.
Again, this is the weird part. When switching to any of the other three styles after this, the inner and outer border properties' callback methods' breakpoints are no longer hit at all. Instead, the breakpoint in the callback method attached to the BorderBrush property is hit an the value of e.NewValue is null. As null values are ignored in the SetBorderBrushes method, no further breakpoints are hit.
After further investigation, I discovered that the e.NewValue was null because there was no default value set on the BorderBrushProperty DependencyProperty. Indeed, after adding a default Brush object to the declaration, this is the Brush that would be passed in e.NewValue in the callback method. Although the breakpoints in the two inner and outer border properties' callback methods would get hit after this, it was only because they are set in the SetBorderBrushes method. The Brush objects set in the BorderInnerBrush and BorderOuterBrush objects in the styles never get passed to these properties after the BorderBrush property has been used once.
One final point to note is that just as the default value of the BorderBrushProperty property is set when a value is not explicitly set in a style, the default values of the inner and outer border DependencyProperty objects are also set when their values are not explicitly set in a style, but only when the BorderBrush property is not also set in the style.
I've been stuck on this for days now and although an easy solution would be to remove the shortcut property, I'd rather find out what's going on and fix it. I hope I've provided enough information for some bright spark to be able to solve this problem, so, if you have any ideas or questions, please share them. Many thanks.
UPDATE >>
Following Rick's suggestion, I created another pair of Brush properties and set them from the callback methods attached to the other three Brush properties. Using this setup, I could switch between all of the styles without any visual problems... or so I thought.
I can now switch from the style that uses the BorderBrush (shortcut) property to the styles that set the two BorderInner and BorderOuter properties and the borders update correctly, so thanks Rick for getting me one step closer. I still have a problem when switching from the style that sets the BorderBrush property to the style that does not explicitly set any border Brush.
Therefore, I still have the same problem that the BorderInnerBrush and BorderOuterBrush properties don't work directly after being set from code behind. What's new is that if I then switch to a style that sets the two Brush properties, this seems to 'wake them up' again... if I then switch to the style that sets no border Brush, the inner and outer properties set their default values correctly again.
It's only after using a style that sets the two border properties from code behind that they become dead to being set in styles or using their default values. This is so strange... can anyone work it out?
UPDATE 2 >>>
First, thanks so much for your time and example Rick. After copying and pasting Rick's example code into a new project, I had to make a few alterations before it would run... maybe because I don't have Expression Blend 4? I installed the SDK and added the references to the noted dlls, but had to use the following XML namespace declarations instead:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
I also had to change each ChangePropertyAction declaration slightly to:
<ei:ChangePropertyAction TargetName="clock" PropertyName="Style" Value="{StaticResource styleBrush}"/>
Then, it worked fine for me and is a good representation of that portion of my actual ClockFace control, with two exceptions. The first is that my styles are switched from code behind via an event handler - this is clearly not causing my problem. The second difference is that I have set default Brush values on my versions of ActualOuterBrush and ActualInnerBrush, so that users of the control don't have to supply border brushes.
So my final problem is that the default inner and outer border brushes do not get set in a Style with no border properties explicitly set after the BorderBrush property has been set in a Style. Remember, the default values do get set up until this point. So, I experimented with Rick's example and added some default values:
private static readonly DependencyPropertyKey ActualInnerBrushPropertyKey = DependencyProperty.RegisterReadOnly("ActualInnerBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata(Brushes.Teal));
private static readonly DependencyPropertyKey ActualOuterBrushPropertyKey = DependencyProperty.RegisterReadOnly("ActualOuterBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata(Brushes.Salmon));
Now this project has a similar problem... the default values are never reset. I'm so baffled by this behaviour.
I've spent so long stuck on this problem now and although I have learned a number of useful things from Rick, I still have this problem with the default values. I've decided that the simplest thing to do is to remove the shortcut BorderBrush property. Rick has helped so much that I am awarding his answer as the correct answer, but if anyone reading this can help with the default value, I'd be grateful to hear from them.
Once a property has been set, styles no longer will be applied. By setting the inner and outer brush properties in your changed handler yourself, the dependency property subsystem is not aware that the the change was actually due to a style versus you doing it explicitly.
One solution is to expose protected read-only ActualInnerBorderBrush and ActualOuterBorderBrush properties and then have all three of the user-definable properties set these actual values in their respective change handlers. This way the user-visible properties can always be "as set by the user" without interfering with each other.
Edit:
Here is a complete working implementation of the five properties:
public class Clock : StackPanel
{
public Brush Brush
{
get { return (Brush)GetValue(BrushProperty); }
set { SetValue(BrushProperty, value); }
}
public static readonly DependencyProperty BrushProperty =
DependencyProperty.Register("Brush", typeof(Brush), typeof(Clock),
new UIPropertyMetadata((d, e) => (d as Clock).OnBrushChanged(d, e)));
public void OnBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ActualInnerBrush = e.NewValue as Brush;
ActualOuterBrush = e.NewValue as Brush;
}
public Brush InnerBrush
{
get { return (Brush)GetValue(InnerBrushProperty); }
set { SetValue(InnerBrushProperty, value); }
}
public static readonly DependencyProperty InnerBrushProperty =
DependencyProperty.Register("InnerBrush", typeof(Brush), typeof(Clock),
new UIPropertyMetadata((d, e) => (d as Clock).OnInnerBrushChanged(d, e)));
public void OnInnerBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ActualInnerBrush = e.NewValue as Brush;
}
public Brush OuterBrush
{
get { return (Brush)GetValue(OuterBrushProperty); }
set { SetValue(OuterBrushProperty, value); }
}
public static readonly DependencyProperty OuterBrushProperty =
DependencyProperty.Register("OuterBrush", typeof(Brush), typeof(Clock),
new UIPropertyMetadata((d, e) => (d as Clock).OnOuterBrushChanged(d, e)));
public void OnOuterBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ActualOuterBrush = e.NewValue as Brush;
}
public Brush ActualInnerBrush
{
get { return (Brush)GetValue(ActualInnerBrushProperty); }
private set { SetValue(ActualInnerBrushPropertyKey, value); }
}
private static readonly DependencyPropertyKey ActualInnerBrushPropertyKey =
DependencyProperty.RegisterReadOnly("ActualInnerBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata());
public static readonly DependencyProperty ActualInnerBrushProperty = ActualInnerBrushPropertyKey.DependencyProperty;
public Brush ActualOuterBrush
{
get { return (Brush)GetValue(ActualOuterBrushProperty); }
private set { SetValue(ActualOuterBrushPropertyKey, value); }
}
private static readonly DependencyPropertyKey ActualOuterBrushPropertyKey =
DependencyProperty.RegisterReadOnly("ActualOuterBrush", typeof(Brush), typeof(Clock), new UIPropertyMetadata());
public static readonly DependencyProperty ActualOuterBrushProperty = ActualOuterBrushPropertyKey.DependencyProperty;
}
and here is a little test program to prove that it works:
<Grid>
<Grid.Resources>
<Style x:Key="styleBrush" TargetType="local:Clock">
<Setter Property="Brush" Value="Red"/>
</Style>
<Style x:Key="styleInnerOnly" TargetType="local:Clock">
<Setter Property="InnerBrush" Value="Green"/>
</Style>
<Style x:Key="styleInnerOuter" TargetType="local:Clock">
<Setter Property="InnerBrush" Value="Blue"/>
<Setter Property="OuterBrush" Value="Yellow"/>
</Style>
<Style x:Key="styleEmpty" TargetType="local:Clock"/>
</Grid.Resources>
<StackPanel>
<local:Clock x:Name="clock" Orientation="Horizontal">
<Rectangle Width="100" Height="100" Fill="{Binding ActualInnerBrush, ElementName=clock}"/>
<Rectangle Width="100" Height="100" Fill="{Binding ActualOuterBrush, ElementName=clock}"/>
</local:Clock>
<StackPanel Orientation="Horizontal">
<Button Content="Default">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{x:Null}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="BrushOnly">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleBrush}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="InnerOnly">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleInnerOnly}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="InnerOuter">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleInnerOuter}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button Content="Empty">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction TargetObject="{Binding ElementName=clock}" PropertyName="Style" Value="{StaticResource styleEmpty}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</StackPanel>
</StackPanel>
</Grid>
This example uses behaviors. If you are not familiar with behaviors, install the Expression Blend 4 SDK and add these namespaces:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
and add System.Windows.Interactivity and Microsoft.Expression.Interactions to your project.
How can I define one global GridViewColumnHeader.Click handler for any ListView.GridViewColumnHeader in my project?
I mean is it possible to set a Style or a Template in app.xaml for TargetType=GridViewColumnHeader so any columnheader in any listview in the project would response to the method in app.xaml.cs?
Though it isn't a global event handler, I would simply create a new control that inherits from ListView and implement the Click handler there.
Yes, with one caveat: You can create a Style that applies to all GridViewColumnHeaders, but you cannot set the Click event in it. However you can set the Command property which has almost exactly the same result:
<Application.Resources>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Command"
Value="{x:Static local:GridViewClickHandler.ClickCommand}" />
</Style>
...
Now it is only required to create the command, register a class handler, and write the code to handle it:
public GridViewClickHandler
{
public RoutedCommand ClickCommand;
static GridViewClickHandler()
{
ClickCommand = new RoutedCommand("ClickCommand", typeof(GridViewClickHandler));
CommandManager.RegisterClassCommandBinding(
typeof(GridViewColumnHeader),
new CommandBinding(ClickCommand, OnColumnHeaderClick));
}
static void OnColumnHeaderClick(object sender, ExecutedRoutedEventArgs e)
{
// your code here
}
}
Note that if you manually set the GridViewColumnHeader's Command property anywhere else in your application it will take precedence over the style. If this is a concern, you may want to instead catch tunneling PreviewMouseDown events at your Window and check each to see if it's original source is a GridViewColumnHeader.