I have the following attached property definition:
public class TestFocusManager
{
public static readonly DependencyProperty FocusedElementProperty =
DependencyProperty.RegisterAttached("FocusedElement",
typeof (UIElement), typeof(TestFocusManager));
public static UIElement GetFocusedElement(DependencyObject obj)
{
return (UIElement) obj.GetValue(FocusedElementProperty);
}
public static void SetFocusedElement(DependencyObject obj, UIElement value)
{
obj.SetValue(FocusedElementProperty, value);
}
}
When I try to use it in my user control:
<UserControl 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:sys="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
xmlns:Behaviors="clr-namespace:MyLocalProject.Behaviors"
Behaviors:TestFocusManager.FocusedElement="{Binding ElementName=testElement}"
x:Class="LocalProject.TestView"
x:Name="_testView">
<TextBox x:Name="testElement" />
</UserControl>
The attached property always returns a null...
var result = TestFocusManager.GetFocusedElement(_testView); // <-- null...
var result2 = _testView.GetValue(TestFocusManager.FocusedElementProperty); // <-- again, null...
What am I doing wrong here? Thanks in advance!
Your problem is that GetFocusedElement is called before the binding is actually set (you're probably calling it in the UserControl's constructor). If you call it in the Loaded event, it should be fine.
I tested your code and it works fine for me except you omitted the "public" keyword in your Dependency Property setter.
I'm assuming that's a typo, if not then that's you're problem.
local:TestFocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
you can get a reference to the user control itself and expose a public property which gives out a button. You can use this as a last resort !.
Btw whenever I have had issues with attached properties. I generally tend to put a callback or change the type to that of Object, I mean instead of UIElement tend to use object atleast I get a callback and check whats the exact type comes as a part of callback
Cheers
Related
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.
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
I have a resource defined in my xaml :
<core:WidgetBase xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" x:Class="....Silverlight.LiquidityConstraintsView"
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:core="clr-namespace:...Silverlight;assembly=....Silverlight"
xmlns:local="clr-namespace:....Silverlight"
mc:Ignorable="d">
<core:WidgetBase.Resources>
<SolidColorBrush x:Key="..." />
</core:WidgetBase.Resources>
...
I am trying to set it from code :
void _administrationClient_GetByFilterModuleSettingCompleted(object sender, GetByFilterModuleSettingCompletedEventArgs e)
{
this.Resources["..."] = new SolidColorBrush(Colors.Red);
}
But I get the error :
The method or operation is not implemented.
stack trace :
at System.Windows.ResourceDictionary.set_Item(Object key, Object value)
at ....Silverlight.LiquidityConstraintsView._administrationClient_GetByFilterModuleSettingCompleted(Object sender, GetByFilterModuleSettingCompletedEventArgs e)
at ....Service.AdministrationServiceClient.OnGetByFilterModuleSettingCompleted(Object state)
It happens when I send off a request to a server to fetch me a colour, then when it returns I try and set that colour to the resource, it fails even if I try and set it as red at that point.
If it at all helps, the method in which I am setting this is an async callback method from WCF call to a server.
If you look at the setter for ResourceDictionary in Reflector (for Silverlight), you'll see it throws a NotImplementedException, so this will not work in Silverlight.
You could try removing the resource and re-adding it, but that's a shot in the dark.
"This indexer implementation specifically blocks a "set" usage. If you attempt to set a value using the indexer, an exception is thrown. You must remove and re-add to the ResourceDictionary in order to change a key-value pair."
http://msdn.microsoft.com/en-us/library/ms601221(v=vs.95).aspx
This operation will work as expected if you try it in a new WPF Application:
<Window.Resources>
<SolidColorBrush x:Key="Brush" Color="Aqua" />
</Window.Resources>
public MainWindow()
{
this.Resources["Brush"] = new SolidColorBrush(Colors.Green);
InitializeComponent();
}
Therefore, I suggest to you that your problem lies elsewhere.
UPDATE >>>
How about avoiding this problem altogether and simply using a public property in your MainWindow.xaml.cs?
In MainWindow.xaml.cs:
public SolidColorBrush Brush { get; set; }
Then anywhere in your application, you should be able to access this property like this:
((MainWindow)App.Current.MainWindow).Brush = new SolidColorBrush(Colors.Red);
Let's say I have a custom control which wraps another control (for example MyCustomButton). I expose a property Content, which wraps the inner control:
public object Content
{
get { return innerControl.Content; }
set { innerControl.Content = value; }
}
In order for a consumer to bind to this property, I need to define a DependencyProperty for it:
public static DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof (object), typeof (MyCustomButton));
but now I need my property definition to use GetValue/SetValue:
public object Content
{
get { return GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
so I'm not wrapping the value of the inner control anymore.
I can define PropertyMetadata to handle the PropertyChanged event of the DependencyProperty, but then I need a bunch of plumbing code to keep the values in sync and prevent infinite loopbacks on changed.
UPDATE: I can't just derive from Button because my UserControl has various other concerns.
Is there a better way to do this?
Well, depending on the particulars of why you're wrapping a button with a user control, you could define a custom control that inherits from button. Then, instead of wrapping the button and exposing the wrapped methods and properties that you want, you can simply override methods and properties whose behavior you want to define the custom control. This way, you'll get all of the functionality of button without the need to reinvent the wheel.
Here's a google link that walks you through it (one of the first that I found - there are plenty): http://knol.google.com/k/creating-custom-controls-with-c-net#
If the user control has other concerns, this may not be an option for you, but I'm offering this answer because the only purpose that you've mentioned for it is wrapping the button. I'd personally favor creating a custom control and inheriting rather than a user control and wrapping if the control in question is simply meant to be a more specific kind of wrapped/inherited control (i.e. button in your case).
Edit: In light of updated question...
You could do something along these lines. Here is the XAML of the client of your user control:
<Grid>
<local:MyControl ButtonContent="Click Me!"/>
</Grid>
</Window>
Here is the XAML for the user control itself:
<UserControl x:Class="GuiScratch.MyControl"
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:GuiScratch"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<StackPanel>
<ContentControl Content="Asdf"/>
<Button Content="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MyControl}},Path=ButtonContent}"/>
</StackPanel>
</Grid>
</UserControl>
And, here is the code behind:
public partial class MyControl : UserControl
{
public static readonly DependencyProperty ButtonContentProperty =
DependencyProperty.Register("ButtonContent", typeof(object), typeof(MyControl),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender));
public object ButtonContent
{
get { return (object)GetValue(ButtonContentProperty); }
set { SetValue(ButtonContentProperty, value); }
}
public MyControl()
{
InitializeComponent();
}
}
So, you don't need to handle the binding at all through code. Your client XAML binds to your dependency property, as does the XAML of the user control itself. In this fashion, they share the dependency property setting. I ran this in my little scratchpad, and the result is (at least my understanding of) what you're looking for. The main window displays the user control as a stack panel with the text "Asdf" and then a button with the text "Click Me!"
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.