AvalonDock - Bind MenuItem to State of DockableContent - c#

I'm using AvalonDock to layout my application.
I want to create a "View" MenuItem with a checkable MenuItem for each of my DockableContents that will show/hide each item.
I'm not finding an example of anyone doing this, and it appears to me the State property is readonly, making it not possible to create a 2-way binding to the MenuItem. It also looks like you have to call methods to change the State.
Anyone have a clever way to do this with bindings? Or is there a simple way to do it I'm missing.

One possible solution is to use an attached property. The attached property would call the necessary methods to change the state. You could then bind to that.
public static class ContentAttach
{
public static readonly DependencyProperty StateProperty = DependencyProperty.RegisterAttached(
"State", typeof(DockableContentState), typeof(ContentAttach), new PropertyMetadata(StateChanged));
public static void SetState(DockableContent element, DockableContentState value)
{
element.SetValue(StateProperty, value);
}
public static DockableContentState GetState(DockableContent element)
{
return (DockableContentState)element.GetValue(StateProperty);
}
private static void StateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var element = (DockableContent)d;
var state = (DockableContentState)e.NewValue;
switch (state)
{
// Call methods in here to change State.
}
}
}

Related

Which is the difference between to use a dependecy property as command and to use a custom routed event?

I have a user control with a datagrid. I would like to notify to the main view that a user doble click in an intem of this datagrid. I would like to notify the doble clicked item too.
For that, the first solution that I thought it was to use a custom routed event, how I could see in the Microsoft documentation.
But I have seen that another option it is to define a dependency property that is a ICommand. This it would be the code:
void RaiseItemDobleClickEvent(object sender, MouseButtonEventArgs e)
{
ItemDobleClickCommand.Execute(null);
}
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("ItemDobleClickCommand", typeof(ICommand), typeof(Window011UC1));
// Declare a get accessor method.
public static ICommand GetItemDobleClickCommand(UIElement target) => (ICommand)target.GetValue(CommandProperty);
// Declare a set accessor method.
public static void SetItemDobleClickCommand(UIElement target, ICommand value) => target.SetValue(CommandProperty, value);
public ICommand ItemDobleClickCommand
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register("ItemDobleClickCommandParameter", typeof(object), typeof(Window011UC1));
public object ItemDobleClickCommandParameter
{
get { return (object)GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
}
}
}
Then in the main view I could use it in this way:
<Label Content="DoubleClick UC2" MouseDoubleClick="{Binding RaiseItemDobleClickEvent}"/>
I see that it is two ways to do the same, but I don't know which are the differences, when to use one solution or another or the advantages or disadvantages of each one.
So when to use a depency property as command and when to use a routed event?
Thanks.
Well, en event is an event and the other approach is commonly referred to as an attached behaviour and it's frequently used in MVVM apps.
You cannot bind an event to a property like this though:
MouseDoubleClick="{Binding RaiseItemDobleClickEvent}"
What you do is that you define a custom dependency property and register a callback that invokes the command. You'll find an example here.
The XAML would look something like this:
<Label ... local:YourClass.MouseDoubleClickCommand="{Binding YourViewModelCommand}" />

Custom control binding setter not firing

I have a custom control with following code:
public partial class TableSelectorControl : UserControl
{
private Brush _cellHoverBrush = new SolidColorBrush(Colors.CadetBlue) { Opacity = 0.3 };
public static readonly DependencyProperty ActiveSelectionProperty =
DependencyProperty.Register("ActiveSelection", typeof(TableSelectorSelection),
typeof(TableSelectorControl));
public TableSelectorSelection ActiveSelection
{
get => (TableSelectorSelection)GetValue(ActiveSelectionProperty);
set
{
SetValue(ActiveSelectionProperty, value);
_cellHoverBrush = value.HoverBrush;
}
}
}
As you can see, I'm trying to set _cellHoverBrush on each ActiveSelectionProperty update, which is done from ViewModel. Binding works well and the ActiveSelectionProperty seemes to change, but the setter is not firing. I surely can use a FrameworkProperyMetadata, but I don't want _cellHoverBrush to become static, the idea is to change it with respect to selected ActiveSelection. How can I achieve this?
I can provide more info, if needed.
There are two types of properties in WPF: .NET Framework properties and dependency properties (which are specific for WPF). Each dependency property has associated a .Net Framework property, but this property is only a wrapper over WPF dependencies properties. This is done to standardize the way we work with properties in WPF. When a dependency property is used in bindings from .xaml files, the WPF framework will not use the .Net wrapper property to get or set the value. This is why, it's not indicated to use other code than GetValue and SetValue in your .NET wrapper property.
For what you need, you should use PropertyChangedCallback, like in the example below:
public partial class TableSelectorControl : UserControl
{
private Brush _cellHoverBrush = new SolidColorBrush(Colors.CadetBlue) { Opacity = 0.3 };
public static readonly DependencyProperty ActiveSelectionProperty =
DependencyProperty.Register("ActiveSelection", typeof(TableSelectorSelection),
typeof(TableSelectorControl), new PropertyMetadata(new PropertyChangedCallback(OnActiveSelectionChanged)));
public TableSelectorSelection ActiveSelection
{
get => (TableSelectorSelection)GetValue(ActiveSelectionProperty);
set => SetValue(ActiveSelectionProperty, value);
}
private static void OnActiveSelectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var tableSelCtrl = d as TableSelectorControl;
if (tableSelCtrl != null)
{
tableSelCtrl._cellHoverBrush = (e.NewValue as TableSelectorSelection)?.HoverBrush;
}
}
}
Using the PropertyChangedCallback of FrameworkPropertyMetadata doesn't necessarily mean you need to make your field static. Your handler method will get a reference to the instance that is invoking it which you can then modify - you will need to cast it to your type first though.
The PropertyChanged walkthrough on this page shows one way you might do it.
https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/dependency-property-callbacks-and-validation

Correct way to check for property change in design mode C# WPF?

Ok, so I'm trying to run some code that modifies a UI when a user changes a custom control's dependency property value in design mode; but only when in design mode.
I've tried these approaches:
1.
public static DependencyProperty x = ...Register(..., new PropertyMetadata(null, changeMethod));
2.
set { SetValue(XProp, value); changeMethod(value); }
3.
var observable = x as INotifyPropertyChanged;
observable.PropertyChanged += ObservablePropertyChanged;
But all of them seem to have their own issues in that they either trigger errors or don't work at all.
So does anyone know what the correct way to listen to a dependency property change in design mode is, and if so can you give an example?
The right way to handle DependencyProperty changes is to:
. Declare the DependencyProperty:
public static DependencyProperty MyXProperty;
. Create the public get/set Property:
public string MyX
{
get { return (string)GetValue(MyXProperty); } //Supposing that the property type is string
set { SetValue(MyXProperty, value); }
}
. Register the DependencyProperty in your static constructor:
static MyClass()
{
MyXProperty= DependencyProperty.Register("MyX", typeof(string), typeof(MyClass), new FrameworkPropertyMetadata("", OnMyXPropertyChanged));
}
. Declare the Property Changed Method:
private static void OnMyXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MyClass thisClass = d as MyClass ;
//Do Something
}
Please provide more information if you still can't find your solution.

Inheritance of Attached Behaviors

I'd like to implement a set of similar attached behaviors for use in a WPF application.
Since their all share a chunk of boilerplate code, that I don't really want to repeat for every one, I'd like to create a base behavior inherit from it.
But since everything inside attached behaviors is static, I am at a loss of how to do it.
As an example, take this behavior, which executes a method on mousedown (the real behaviors would of course do something not easily done in an eventhandler):
public static class StupidBehavior
{
public static bool GetIsEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(DependencyObject obj, bool value)
{
obj.SetValue(IsEnabledProperty, value);
}
// Using a DependencyProperty as the backing store for ChangeTooltip. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(StupidBehavior), new UIPropertyMetadata(false, IsEnabledChanged));
private static void IsEnabledChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
((UIElement)sender).MouseDown += { (o,e) => MyMethod(); };
}
private static void MyMethod()
{
MessageBox.Show("Boo");
}
}
Now, I'd like to create a new behavior that should have a different implementation of MyMethod, as well as a few additional properties controlling it. How should this be done?
You could create another attached property which contains the detailed implementation which is being called by the main behavior as a sub-class replacement. The object that property holds could be non-static and be used like a state-object.
(You could probably fit this into one property as well, where property == null means off)
You could use a static constructor to form a Dictionary<DependencyProperty,EventHandler> to map the specific DP to a specific handler and use a common DependencyPropertyChanged callback:
static StupidBehavior()
{
handlerDictionary[IsEnabledProperty] = (o,e) => MyMethod();
handlerDictionary[SomeOtherProperty] = (o,e) => SomeOtherMethod();
}
private static void CommonPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
var uie = sender as UIElement;
if (uie != null)
{
//removing before possibly adding makes sure the multicast delegate only has 1 instance of this delegate
sender.MouseDown -= handlerDictionary[args.Property];
if (args.NewValue != null)
{
sender.MouseDown += handlerDictionary[args.Property];
}
}
}
Or simply do a switch on args.Property. Or something in-between that involves a common method and branching based on the DependencyProperty.
And I'm not sure why your IsEnabled property deals with a value of type DependencyProperty rather than something that would make more semantic sense like bool.

Wrapped WPF Control

I'm trying to create a GUI (WPF) Library where each (custom) control basically wraps an internal (third party) control. Then, I'm manually exposing each property (not all of them, but almost). In XAML the resulting control is pretty straightforward:
<my:CustomButton Content="ClickMe" />
And the code behind is quite simple as well:
public class CustomButton : Control
{
private MyThirdPartyButton _button = null;
static CustomButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomButton), new FrameworkPropertyMetadata(typeof(CustomButton)));
}
public CustomButton()
{
_button = new MyThirdPartyButton();
this.AddVisualChild(_button);
}
protected override int VisualChildrenCount
{
get
{ return _button == null ? 0 : 1; }
}
protected override Visual GetVisualChild(int index)
{
if (_button == null)
{
throw new ArgumentOutOfRangeException();
}
return _button;
}
#region Property: Content
public Object Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public static readonly DependencyProperty ContentProperty = DependencyProperty.Register(
"Content", typeof(Object),
typeof(CustomButton),
new FrameworkPropertyMetadata(new PropertyChangedCallback(ChangeContent))
);
private static void ChangeContent(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
(source as CustomButton).UpdateContent(e.NewValue);
}
private void UpdateContent(Object sel)
{
_button.Content = sel;
}
#endregion
}
The problem comes after we expose MyThirdPartyButton as a property (in case we don't expose something, we would like to give the programmer the means to use it directly). By simply creating the property, like this:
public MyThirdPartyButton InternalControl
{
get { return _button; }
set
{
if (_button != value)
{
this.RemoveVisualChild(_button);
_button = value;
this.AddVisualChild(_button);
}
}
}
The resulting XAML would be this:
<my:CustomButton>
<my:CustomButton.InternalControl>
<thirdparty:MyThirdPartyButton Content="ClickMe" />
</my:CustomButton.InternalControl>
And what I'm looking for, is something like this:
<my:CustomButton>
<my:CustomButton.InternalControl Content="ClickMe" />
But (with the code I have) its impossible to add attributes to InternalControl...
Any ideas/suggestions?
Thanks a lot,
--
Robert
WPF's animation system has the ability to set sub-properties of objects, but the XAML parser does not.
Two workarounds:
In the InternalControl property setter, take the value passed in and iterate through its DependencyProperties copying them to your actual InternalControl.
Use a build event to programmatically create attached properties for all internal control properties.
I'll explain each of these in turn.
Setting properties using the property setter
This solution will not result in the simplified syntax you desire, but it is simple to implement and will probably solve the main problem with is, how to merge values set on your container control with values set on the internal control.
For this solution you continue to use the XAML you didn't like:
<my:CustomButton Something="Abc">
<my:CustomButton.InternalControl>
<thirdparty:MyThirdPartyButton Content="ClickMe" />
</my:CustomButton.InternalControl>
but you don't actually end up replacing your InternalControl.
To do this, your InternalControl's setter would be:
public InternalControl InternalControl
{
get { return _internalControl; }
set
{
var enumerator = value.GetLocalValueEnumerator();
while(enumerator.MoveNext())
{
var entry = enumerator.Current as LocalValueEntry;
_internalControl.SetValue(entry.Property, entry.Value);
}
}
}
You may need some additional logic to exclude DPs not publically visible or that are set by default. This can actually be handled easily by creating a dummy object in the static constructor and making a list of DPs that have local values by default.
Using a build event to create attached properties
This solution allows you to write very pretty XAML:
<my:CustomButton Something="Abc"
my:ThirdPartyButtonProperty.Content="ClickMe" />
The implementation is to automatically create the ThirdPartyButtonProperty class in a build event. The build event will use CodeDOM to construct attached properties for each property declared in ThirdPartyButton that isn't already mirrored in CustomButton. In each case, the PropertyChangedCallback for the attached property will copy the value into the corresponding property of InternalControl:
public class ThirdPartyButtonProperty
{
public static object GetContent(...
public static void SetContent(...
public static readonly DependencyProperty ContentProperty = DependencyProperty.RegisterAttached("Content", typeof(object), typeof(ThirdPartyButtonProperty), new PropertyMetadata
{
PropertyChangedCallback = (obj, e) =>
{
((CustomButton)obj).InternalControl.Content = (object)e.NewValue;
}
});
}
This part of the implementation is straightforward: The tricky part is creating the MSBuild task, referencing it from your .csproj, and sequencing it so that it runs after the precompile of my:CustomButton so it can see what additional properties it needs to add.

Categories

Resources