I have the following singleton class which contains the following property:
public class Manager : INotifyPropertyChanged
{
public bool IsOnline
...
public static Manager Instance
...
}
In the mark-up I am trying to change the color of a button based on this online property:
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding Source="{x:Static storage:Manager.Instance}" Path="IsOnline"/>
</DataTrigger.Binding>
<Setter Property="Background" Value="#8000FF00"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
This binding <Binding Source="{x:Static storage:Manager.Instance}" Path="IsOnline"/> fails with the exception:
Cannot convert the value in attribute 'Source' to object of type 'System.Windows.Markup.StaticExtension'.
I have quadruple-checked the "storage" namespace; I know it is both referenced and correct. The Instance property is static, so I do not understand why this binding would fail. I have similar bindings to static properties all over that work just fine.
Any ideas?
I've built a sample app that does exactly what you're describing, it works with no issues. You can download it here.
Have you tried using a ValueConverter to inspect the value that the StaticExtension is getting?
(See method 2 on this page)
I just had exactly the same weird problem!
Solution:
You MUST instantiate the class before doing any XAML operations with that class!
Related
Suppose the object I'm binding to has a property with a string representing the ResourceKey - how do I get a StaticResource to dynamically acquire it's ResourceKey based on a binding to the underlying object?
I want something equivalent to this
MyProperty="{StaticResource ResourceKey={Binding Path=MyProperty}}"
While this compiles, it will fail complaining it can't find a key of type System.Windows.Data.Binding
I don't need dynamic re-evaluation if the underlying value changes (eg DynamicResource)
I found exactly what I was looking for here:
http://blog.functionalfun.net/2009/12/specifying-resource-keys-using-data_31.html
It's a custom markup extension that behaves like a regular binding but the Path will point to a property that transforms the target into a ResourceKey and the binding will then return the resource!
I've found this extremely useful!
It's also possible to do it in the XAML code
<MyControl.Resources>
<Image x:Key="Image_1" Source="{StaticResource ResourceKey={x:Static img:ImageResourcesKeys.Image_1}}" x:Shared="false"/>
<Image x:Key="Image_2" Source="{StaticResource ResourceKey={x:Static img:ImageResourcesKeys.Image_2}}" x:Shared="false"/>
<Style TargetType="MyProperty">
<Style.Triggers>
<DataTrigger Binding="{Binding ImageType}" Value="Image_1">
<Setter Property="MyProperty" Value="{StaticResource Image_1}" />
</DataTrigger>
<DataTrigger Binding="{Binding ImageType}" Value="Image_2">
<Setter Property="MyProperty" Value="{StaticResource Image_2}" />
</DataTrigger>
</Style.Triggers>
</Style>
</MyControl.Resources>
All you need is a string property ImageType to choose the image you are going to use
So I have some code similar to the following: (Forgive any typos-- I tried to simplify in the SO editor for the post)
<my:CustomContentControl>
<my:CustomContentControl.Style>
<Style TargetType="{x:Type my:CustomContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CurrentView}" Value="MyCustomView">
<Setter Property="Content">
<Setter.Value>
<my:CustomView DataContext="{Binding DataContextForMyCustomView"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</m:CustomContentControl.Style>
</my:CustomContentControl>
The problem is that whenever the DataTrigger occurs, the setter does set the Content property to my:CustomView, but it does not bind DataContext. If I move the same code outside of the trigger the DataContext binding works just fine.
Any ideas? If this is a limitation of some sorts, is there any work around?
Update:
I received the following error in the output window:
System.Windows.Data Error: 3 : Cannot find element that provides DataContext. BindingExpression:Path=DataContextForMyCustomView; DataItem=null; target element is 'CustomView' (Name='customView'); target property is 'DataContext' (type 'Object')
The error you posted makes it sound like your custom control is in an object that doesn't have a DataContext, such as a DataGridColumn.Header.
To get around that, you can create a Freezeable object in your .Resources containing the binding you're looking for, then bind your my:CustomView.DataContext to that object
<my:CustomContentControl.Resources>
<local:BindingProxy x:Key="proxy"
Data="{Binding DataContextForMyCustomView, ElementName=MyControl}" />
</my:CustomContentControl.Resources>
...
<my:CustomView DataContext="{Binding Source={StaticResource proxy}}"/>
Here's the code for a sample Freezable object copied from here:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data.
// This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy), new UIPropertyMetadata(null));
}
Also, you really should use ContentTemplate instead of Content to avoid an exception if more than one object applies that style :)
I solved a similar problem by putting the UserControl into the resources and then changing the Content with that.
e.g. from my own code (different names, same concept)
<ContentControl Grid.Column="1"
Margin="7,0,7,0">
<ContentControl.Resources>
<mapping:Slide11x4MappingView x:Key="Slide11X4MappingView" DataContext="{Binding MappingViewModel}"/>
<mapping:MicrotubeMappingView x:Key="MicrotubeMappingView" DataContext="{Binding MappingViewModel}"/>
</ContentControl.Resources>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Acquirer.Sorter.TrayType}" Value="{x:Static mapping:TrayType.SLIDES11X4}">
<Setter Property="Content" Value="{StaticResource Slide11X4MappingView}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Acquirer.Sorter.TrayType}" Value="{x:Static mapping:TrayType.VIALS}">
<Setter Property="Content" Value="{StaticResource MicrotubeMappingView}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
I want to change the state of a dependency property (Mode) in a UserControl triggered off of a change change of a second dependency property (ViewType).
I tried defining a DataTrigger as follows, but for some reason it doesn't work:
<UserControl.Resources>
<Style TargetType="{x:Type UserControls:MyUserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType}" Value="ViewType2">
<Setter Property="Mode" Value="Logon"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
To verify that my binding was working correctly I wrote the following test XAML in the same user control:
<Style x:Key="butstyle">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType}" Value="ViewType2">
<Setter Property="Control.Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
When I assigned this style to a button, I see the foreground color change to white at the right time, which proves my Binding to ViewType is working.
I seems I need to use a style under usercontrol.resources (like in the first block of code above), but the "Mode" property is never set.
Is this the right way to do this? I'd like to try to keep this in XAML if possible, but I'm not comfortable how triggers should work when they are not related directly to visual elements.
In case its relevant, the "ViewType" bound property is defined in a parent UserControl, and the "Mode" property is defined on MyUserControl.
Update:
I used a converter to find out that my trigger is being called, however my setter is not getting engaged for some reason. Here is the code for my DP which I am trying to set. Breakpoints on neitehr the set() nor the OnModeChanged() are called. Is there anything wrong with my Dependency Property?
public enum ModeStates { Logon, NextState }
public ModeStates Mode
{
get { return (ModeStates)GetValue(ModeProperty); }
set { SetValue(ModeProperty, value); }
}
protected static readonly DependencyProperty ModeProperty = DependencyProperty.Register(
"Mode", typeof(ModeStates), typeof(MyUserControl),
new FrameworkPropertyMetadata(new PropertyChangedCallback(OnModeChanged))
);
private static void OnModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// .......
}
Update:
I was able to use DependencyPropertyDescriptor.AddValueChanged() to solve my problem in a pretty clean fashion.
If the dependency properties in question are defined on the UserControl you need to set it's style property, not add a default style inside the resources.
to set the style inline in XAML
<UserControl.Style>
<Style TargetType="{x:Type UserControls:MyUserControl}">
<!-- Triggers -- >
</Style>
</UserControl.Style>
In addition to Adam's answer, are you setting the Mode property in the <UserControl> tag anywhere? If you do, it is overwriting the triggered value.
Also, on occasion I have had issues with a triggered value not getting set unless a default value is also defined in the style.
For example, sometimes this will not work
<UserControl.Style>
<Style TargetType="{x:Type UserControls:MyUserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType}" Value="ViewType2">
<Setter Property="Mode" Value="Logon"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
while this does
<UserControl.Style>
<Style TargetType="{x:Type UserControls:MyUserControl}">
<Setter Property="Mode" Value="{x:Null}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ViewType}" Value="ViewType2">
<Setter Property="Mode" Value="Logon"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
I have following code in WPF XAML and want it to be converted to Silverlight 4:
<Setter
Property="Background"
Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}" />
<Setter>
Unfortunately, Silverlight does not support x:Static.
Does anybody know how to port it properly without code behind, XAML-only?
Since you cannot access Static properties like that,you've to define your own "wrapper" class that will wrap the static properties, something like this:
public class StaticMemberAccess
{
public ResourceKey WindowBrushKey { return SystemColors.WindowBrushKey; }
//define other wrapper propeties here, to access static member of .Net or your classes
}
Then do this in XAML
<UserControl.Resources>
<local:StaticMemberAccess x:Key="SMA"/>
</UserControl.Resources>
<Setter
Property="Background"
Value="{Binding Source={StaticResource SMA}, Path=WindowBrushKey}" />
<Setter>
Hope, it gives you some idea. :-)
See this also:
Retrieving value from static extension XAML
I am trying to use Validation in WPF. I created a NotNullOrEmptyValidationRule as shown below:
public class NotNullOrEmptyValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
if (String.IsNullOrEmpty(value as String))
return new ValidationResult(false, "Value cannot be null or empty");
return new ValidationResult(true, null);
}
}
Now, I need to use it in my application. In my App.xaml file I declared the Style for the TextBox. Here is the declaration.
<Style x:Key="textBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="Green"/>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="Red"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Now, I want to use it on my TextBox so I am using the following code:
<TextBox Style="{StaticResource textBoxStyle}">
<TextBox.Text>
<Binding>
<Binding.ValidationRules>
<NotNullOrEmptyValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
The error comes on the Tag NotNullOrEmptyValidationRule. The XAML syntax checker is not able to resolve the NotNullOrEmptyValidationRule. I have even tried putting the namespace but it does not seem to work.
You just need to add the xmlns to your Window, and use that to reference your ValidationRule.
In WPF, the object is perfectly fine to be used from the same assembly.
Since your rule isn't defined in the standard XAML namespace, you have to create a mapping to your clr namespace like so:
<Window ...
xmlns:local="clr-namespace:MyNamespaceName">
And then you would use it like so:
<Binding Path=".">
<Binding.ValidationRules>
<local:NotNullOrEmptyValidationRule />
</Binding.ValidationRules>
</Binding>
Edit
I added a Path statement to the Binding. You have to tell the Binding what to bind to :)
i see your binding on the TextBox is set to a path of 'Text' - is that a field on whatever the datacontext of this textbox is? is the textbox actually getting a value put into it? also, if you put a breakpoint in your validation method, is that ever getting fired?
you may want to lookup how to log failures in binding and review those as well..
You do not have this line in ur code behind
Public Sub New()
' This call is required by the Windows Form Designer.
InitializeComponent()
Me.**NameOfTextBox**.DataContext = Me
End Sub
There is a bug in Visual Studio and Expression Blend that causes this problem. What you need to do is make sure that the Validation rule is in a separately project/assembly that you can reference. This should resolve the problem.
However, you will have to add back the namespace in order for it to work.