Calling a custom dependency property defined in code-behind from XAML - wpf

Is it possible to call a custom dependency property in the XAML of the element in which it is defined?
I mean, i have the following simple code for my mainWindow:
Code
public partial class MainWindow : Window
{
public static readonly DependencyProperty SpecialToProperty = DependencyProperty.Register("SpecialTo", typeof(double), typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
}
public double SpecialTo
{
get
{
return (double)GetValue(SpecialToProperty);
}
set
{
SetValue(DoubleAnimation.ToProperty, value);
}
}
}
How can i use that dependency property from the XAML partial code of the MainWindow class?
I mean something like:
<Window x:Class="WpfAnimationTEst.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
SpecialTo=200>
I know it can be done using attached dependency properties, but is it the only way? Is it not possible to call a dependency property defined in the code-behind?
Thank you and sorry if the question is some kind of stupid, i'm just learning and trying to understand WPF.

I found the answer after I initially posted a wrong answer:
The problem really lies in circular dependencies if you use andreask's answer. I had to create a BaseClass for all windows:
1) Create a new Window Base Class:
public class BaseWindow : Window {
public BaseWindow() { }
public static readonly DependencyProperty SpecialToProperty = DependencyProperty.Register("SpecialTo", typeof(double), typeof(BaseWindow));
public double SpecialTo {
get {
return (double)GetValue(SpecialToProperty);
}
set {
SetValue(SpecialToProperty, value);
}
}
}
This will be the new baseclass for all your windows.
2) Modify your MainWindow xaml: (Change YOURNAMESPACE (2x) to your namespace name)
<local:BaseWindow x:Class="YOURNAMESPACE.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YOURNAMESPACE"
Title="MainWindow" Height="350" Width="525" SpecialTo="100">
<Grid>
</Grid>
</local:BaseWindow>
3) And you also need to modify your partial MainWindow.cs:
public partial class MainWindow : BaseWindow {
public MainWindow() {
InitializeComponent();
}
}
That worked for me, however, you will always need to use the extra xaml markup in your window declaration.

I'm answering my own question because there seems to be many ways to solve it correctly. I've upvoted the answers that best helped me, but i can't set any as the correct answer since all are correct.
So i'll just post a conclusion. If you think that i'm mistaken, please post a comment and i will correct my mind.
The main answer to my question is no, it is not possible to directly call a custom dependency property defined at code-behind from its "linked" XAML file. It is mandatory to instantiate the control in which the property is defined to call it.
To me, the best workarrounds to use a custom dependency property in XAML, defined in the code-behind are the posted by #Clemens and #Noel Widmer. This and this

You can use custom dependency properties in XAML, but only if you instantiate the control in XAML. For example, take a customized TextBox element:
public class MyTextBox : TextBox
{
public static readonly DependencyProperty SpecialToProperty = DependencyProperty.Register("SpecialTo", typeof(double), typeof(MyTextBox));
public double SpecialTo
{
get
{
return (double)GetValue(SpecialToProperty);
}
set
{
SetValue(DoubleAnimation.ToProperty, value);
}
}
}
You can of course create an instance of MyTextBox in XAML and assign the SpecialTo property there:
<custom:MyTextBox SpecialTo="1.0" />
In your case, however, you're not instantiating the custom class MainWindow, but you create a new instance of class Window, and the Window class isn't aware of the custom dependency property (the SpecialTo property is not even available in Window, since you declared it within the MainWindow class).
For the dependency property to be recognized, you'd need to instantiate MainWindow directly:
<custom:MainWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
SpecialTo=200>
However, this means you need to omit the x:class directive that used to combine XAML and codebehind of your window (otherwise you'd run into circular dependencies), and I'm not sure if this correctly initalizes your window...

Yes, it is possible. Dependency properties are used to bind within XAML. If you want to bind to property defined in the code behind window you need to reference this window as XAML element, i.e. add tag for your main window x:Name="mainWindow", and next in the binding expression refer it as ElementName=mainWindow

Related

How to bind a collection of a subtype to a dependency property

I'm creating a UserControl in WPF, that is able to work for any object of type IMyNode. Basically, it receives an ObservableCollection through a dependency property, register to it and do some stuff.
In one of my usecase, I use in a control that uses(and need), an ObservableCollection of SomeSpecificNode. SomeSpecificNode is an implementation of IMyNode.
Currently, I've a binding error:
System.Windows.Data Error: 1 : Cannot create default converter to perform 'one-way' conversions between types 'System.Collections.ObjectModel.ObservableCollection`1[SomeSpecificNode]' and 'System.Collections.ObjectModel.ObservableCollection`1[IMyNode]'.
I understand why it happens, it doesn't know how to convert automatically an ObservableCollection<SomeSpecificNode> to ObservableCollection<IMyNode>.
What would be the correct approach to do this?
Using a converter would break the NotifyPropertyChange. Using a ObservableCollection<IMyNode> in my parent ViewModel would not work for the other control in the same page.
Thank you!
Here some pseudo code:
public class SomeSpecificNode: IMyNode{
}
public interface IMyNode{
}
public class ParentViewModel {
public ObservableCollection<SomeSpecificNode> SelectedNodes {get;}=> new ObservableCollection<SomeSpecificNode>()
}
<UserControl x:Class="ParentView"
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:ch.VibroMeter.Xms.Configurators.Controls.ActionBar"
xmlns:dxb="http://schemas.devexpress.com/winfx/2008/xaml/bars"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<ParentViewModel/>
</UserControl.DataContext>
<StackPanel Orientation="Horizontal" Name="RootContainer">
<SomeChildControl Nodes="{Binding SelectedNodes}" /><!-- This binding will fail !-->
</StackPanel
</UserControl>
public partial class ParentView : UserControl
{
public static readonly DependencyProperty NodesProperty = DependencyProperty.Register(
nameof(Nodes), typeof(ObservableCollection<IMyNode>), typeof(ParentView), new PropertyMetadata(default(ObservableCollection<IMyNode>), OnNodesChanged));
private static void OnNodesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//...
}
public ObservableCollection<IMyNode> Nodes
{
get { return (ObservableCollection<IMyNode>)GetValue(NodesProperty); }
set { SetValue(NodesProperty, value); }
}
}
You should change the type of the dependency property to a compatible type such as IEnumerable<IMyNode>.
You cannot set an ObservableCollection<IMyNode> property to anything else than an ObservableCollection<IMyNode> or null.
An ObservableCollection<SomeSpecificNode> is not an ObservableCollection<IMyNode> but it is an IEnumerable<IMyNode> assuming that SomeSpecificNode implements IMyNode.
So this compiles just fine;
IEnumerable<IMyNode> collection = new ObservableCollection<SomeSpecificNode>();
But this doesn't:
ObservableCollection<IMyNode> collection = new ObservableCollection<SomeSpecificNode>(); //Cannot implictly convert type...
The difference is that IEnumerable<T> is covariant. Please refer to the docs for more information.

Navigation through button command in viewmodel

I have a WPF application with two pages, now I wanted to navigate to the other page when the button in first the page is clicked (I wrote the command for button in the first page), but the logic should be through the viewmodel. How to achieve this?
When I write WPF applications that need to navigate to different pages, I like to follow Rachel Lim's method to implement it using DataTemplates and ViewModels. You can follow the link to her page to get the exact code for the solution, but I'll give a little summary of her method here.
In her method, she creates a ViewModel that represents the application and has a property called CurrentPage which holds a ViewModel. You can then create a command on the ApplicationViewModel called ChangePage. This command will take the ViewModel that is passed as a parameter and sets it to the CurrentPage.
The xaml takes the responsibility of switching out the correct views. When using this method, I put a ContentControl in my MainWindow and bind the Content property to ApplicationViewModel.CurrentPage. Then in the resources of the MainWindow, I create DataTemplates to tell the view "When I try to display this ViewModel, put that View on the screen".
You don't really provide any code. But I assume your Navigation is in your code behind. You could do this by binding a Command OneWayToSource.
XAML
<local:MainWindow x:Class="WpfNameSpace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfNameSpace"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
NavigatePageCommand="{Binding Path=MyViewModel.NavigateCommand, Mode=OneWayToSource}"
Title="MainWindow" Height="600" Width="800">
<Grid>
</Grid>
</local:MainWindow>
Please take a look at local:MainWindow.
C#
public partial class MainWindow : Window
{
public ICommand NavigatePageCommand
{
get { return (ICommand) GetValue(NavigatePageCommandProperty); }
set { SetValue(NavigatePageCommandProperty, value); }
}
// Using a DependencyProperty as the backing store for NavigatePageCommand. This enables animation, styling, binding, etc...
public static readonly DependencyProperty NavigatePageCommandProperty =
DependencyProperty.Register("NavigatePageCommand", typeof(ICommand), typeof(MainWindow),
new PropertyMetadata(0));
public MainWindow()
{
InitializeComponent();
NavigatePageCommand = new RelayCommand(Navigate);
}
public void Navigate()
{
//Do Navigation here
}
}
I assume you are familiar with Commands, ViewModels and Bindings and you get the idea.

WPF MVVM binding to DependencyProperty of a UserControl not working

I am trying to stick close to MVVM approach to building my WPF app and am running into a weird binding issue and feel like I'm missing something.
I have a user control (PluginsTreeView) which has a ViewModel (PluginsViewModel) driving it. PluginsTreeView exposes a public DependencyProperty of type string (DocumentPath). My MainWindow set's this property in XAML, but it doesn't seem to make it to my UserControl. I'm looking for some indication as to why this doesn't work.
PluginsTreeView.xaml.cs
public partial class PluginsTreeView: UserControl
{
public PluginsTreeView()
{
InitializeComponent();
ViewModel = new ViewModels.PluginsViewModel();
this.DataContext = ViewModel;
}
public static readonly DependencyProperty DocumentPathProperty =
DependencyProperty.Register("DocumentPath", typeof(string), typeof(PluginsTreeView), new FrameworkPropertyMetadata(""));
public string DocumentPath
{
get { return (string)GetValue(DocumentPathProperty); }
set
{
//*** This doesn't hit when value set from xaml, works fine when set from code behind
MessageBox.Show("DocumentPath");
SetValue(DocumentPathProperty, value);
ViewModel.SetDocumentPath(value);
}
}
....
}
MainWindow.xaml
My PluginsTreeView never gets the value 'test path' and I'm not sure why. I feel like I'm missing something fundamental here.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Views="clr-namespace:Mediafour.Machine.EditorWPF.Views" x:Class="Mediafour.Machine.EditorWPF.MainWindow"
xmlns:uc="clr-namespace:Mediafour.Machine.EditorWPF.Views"
Title="MainWindow" Height="350" Width="600">
<Grid>
<uc:PluginsTreeView x:Name="atv" DocumentPath="from xaml" />
</Grid>
</Window>
But, when I set the DependencyProperty from the code-behind of the MainWindow, it does seem to set the value correctly. I'm trying to figure out the difference here and why the code-behind approach works and setting the property in xaml doesn't.
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainWindowViewModel ViewModel = new MainWindowViewModel();
this.DataContext = ViewModel;
atv.DocumentPath = "from code behind"; //THIS WORKS!
}
....
}
Messing around with Snoop, I see that the value from XAML "from xaml" does make it into the property but my Set method in PluginsTreeView still never gets hit. The message box I have in there as a debug tool doesn't pop unless value is set from MainWindow code-behind.
Apparently you should not add any logic to these properties setters, because they are only called when you set the property from code. If you set the property from XAML the SetValue() method is called directly. I've ended up registering a callback method and all works great now:
public static readonly DependencyProperty DocumentPathProperty = DependencyProperty.Register("DocumentPath", typeof(string), typeof(PluginsTreeView), new FrameworkPropertyMetadata("initial value", OnValueChanged));

Set custom window property in xaml

I have the following code:
public partial class NewWindow: Window
{
public static readonly DependencyProperty PropNameProperty =
DependencyProperty.Register(
"PropName",
typeof(int),
typeof(NewWindow),
null);
public int PropName
{
get
{
return (int)GetValue(PropNameDependencyProperty);
}
set
{
SetValue(PropNameDependencyProperty, value);
}
}
Now when I try to use my new property I can't compile:
<Window x:Class="AppName.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:my="clr-namespace:AppName"
Title="NewWindow" Height="300" Width="300"
PropName="5" <-"property does not exist" error here
>
I'm probably just misunderstanding something, but I'm not sure what.
As I understand it, the reason it can't find the property is because it's looking for it in the Window class, not in your NewWindow class. Why? Because the XAML tag name is Window, not NewWindow.
I tried changing the tag to NewWindow, but you can't actually do that, because your XAML and the code behind are cooperating to define the NewWindow class and you can't define a class in terms of itself. This is why the toplevel XAML element is always the parent class, and this suggests a solution: define the property in a new class which inherits from Window (call it, for the sake of argument, ParentWindow), and then derive NewWindow from that, so you get something like
<local:ParentWindow x:Class="TestApp.NewWindow"
PropName="5"
xmlns:local="clr-namespace:TestApp"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
...
</local:ParentWindow>
I appreciate this is not necessarily a very elegant solution.

Expose UserControl property to XAML

WPF controls have certain properties (UserControl.Resources, UserControl.CommandBindings) that can have items added to them from the XAML of a user control declaration. Example:
<UserControl ... >
<UserControl.CommandBindings>
...
</UserControl.CommandBindings>
<UserControl.Resources>
...
</UserControl.Resources>
</UserControl>
I have a new list property defined in my user control:
public partial class ArchetypeControl : UserControl {
...
public List<Object> UICommands { get; set; }
I want to add items to this list like I can with resources and CommandBindings, but when I do this:
<c:ArchetypeControl.UICommands>
</c:ArchetypeControl.UICommands>
I get the error "Error 4 The attachable property 'UICommands' was not found in type 'ArchetypeControl'. "
Suggestions?
-
Given the comments, I've created a test control to show the entire code and reproduce the problem. I'm using visual studio 2010.
<UserControl x:Class="ArchetypesUI.TestControl"
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:c="clr-namespace:ArchetypesUI"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<c:TestControl.TestObject>
</c:TestControl.TestObject>
<Grid>
</Grid>
</UserControl>
-
namespace ArchetypesUI
{
/// <summary>
/// Interaction logic for TestControl.xaml
/// </summary>
public partial class TestControl : UserControl
{
public Object TestObject { get; set; }
public TestControl()
{
InitializeComponent();
}
}
}
Now the error I get is "Error 2 The attached property 'TestControl.TestObject' is not defined on 'UserControl' or one of its base classes."
Take a look at your XAML:
<UserControl>
^^^^^^^^^^^
<c:TestControl.TestObject>
^^^^^^^^^^^
</c:TestControl.TestObject>
</UserControl>
Here, you are declaring a UserControl, and then trying to set a TestControl property on it. Since UserControl doesn't have the TestControl.TestObject property, WPF that it can't set that property on the UserControl object. You may say, "But I'm declaring a UserControl of type TestControl. My UserControl is a TestControl!" But that's not quite the case. The above declaration is declaring the TestControl class: it's not creating an instance of TestControl, so it can't have instance properties set on it.
Rather, the TestObject property is there for users of TestControl to set on individual instances of TestControl:
<local:TestControl>
<local:TestControl.TestObject> <!-- Now it will work -->
</local:TestControl.TestObject>
</local:TestControl>
If you want to set a default / initial value for the TestObject property, then you can do so either in the TestControl constructor, or (if TestObject is a dependency property) through the TestControl default style (though this is more for custom controls than for user controls).
I'm not quite able to recreate your issue... the case I've created seems to work. I did have to initialize the list in the constructor.
However, from your example I wonder a more appropriate place for your list source would be on a ViewModel object of some sort. If you're exposing commands, having an IEnumerable of some sort of a ICommand wrapper which also encapsulates the display elements you need (e.g. Caption, Icon URI, etc).
ViewModels are certainly not a panacea, but in this case I think it would let you put all the knowledge of the commands you want to use in the same place (e.g. which are available and what they do).

Resources