I have a style with a Trigger for IsMouseOver, and I want to trigger that Trigger (heh) from C#. How can this be done? Thanks in advance!
You can create a DataTrigger and then bind to a property in your ViewModel.
First create a bool property that will advise the View whether it should 'show' or 'not show' your style.
public bool GridTrigger
{
get { return this.gridTrigger; }
set { // raise a PropertyChange event, as per normal
}
And then add the DataTrigger to the XAML
<Grid.Triggers>
<DataTrigger Binding="{Binding GridTrigger}" Value="True">
<Setter Property="Style" Value="{StaticResource MyMouseOverStyle"/>
</DataTrigger>
</Grid.Triggers>
This obviously assumes you are using MVVM!
Related
When I have a control A that contains a control B, there are properties Prop that are inherited. That means, B.Prop will automatically take the value of A.Prop if B.Prop is not explicitly set. As far as I know, IsEnabled is such a property.
Now I have a situation where I do set the value of B.IsEnabled explicitly, and still it is overwritten by the value of A.IsEnabled. Why is that so, and how can I correct it?
In this situation A is a StackPanel and B a TextBox:
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding InDisableMode}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBox Text="some text">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding InDisableMode}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</StackPanel>
The above XAML snippet has its DataContext set to my ViewModel. The ViewModel contains a property InDisableMode which is a bool. When it is false, everything is as expected: The StackPanel is enabled and the TextBox is disabled.
But when InDisableMode is true, both the StackPanel and the TextBox are disabled although both Triggers should trigger!
Note: I know I can databind IsEnabled to InDisableMode in both controls (in the TextBox directly, in the StackPanel by using a complement converter). I have not tried if this works since I want to do this with Triggers anyway.
EDIT:
The point of disabling the StackPanel is to disable all of its children easily (except the TextBox which I want to enable instead). Any other ideas how to solve this task without changing the parent-child-relationship or creating new controls? At the moment, the only way I see is to disable all children except the TextBox one by one...
If you disable a control, its children are disabled. Since a StackPanel is not interactive, there is no reason to disable it other than to disable its interactive children.
If you want to enable a control A while its parent B is disabled, you can't do that. B cannot be the parent if you want to enable A while B is disabled.
For a workaround, you can put them both in a Grid, with the TextBox defined last, to superimpose the TextBox on top of the StackPanel. Then it will be within the StackPanel's area but it won't be a child of the StackPanel.
This happens because UIElement.IsEnabled property uses value coercion by inheriting the value from its parent. This it does by using CoerceValueCallback. Value coercion is ranked first in Dependency Property Setting Precedence List.
So, to override this behavior, we have two options. Firstly, to use AddOwner() to register our type as new owner of IsEnabled property. Secondly, to override the metadata using OverrideMetadata(). This second method would work only if you inherit directly from UIElement.
So, lets say we want our Button to behave differently, we should create a new Button like below :
public class CButton : Button
{
public static readonly DependencyProperty IsEnabled;
static CButton()
{
IsEnabled = UIElement.IsEnabledProperty.AddOwner(typeof(CButton),
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.None,
UIElement.IsEnabledProperty.DefaultMetadata.PropertyChangedCallback,
new CoerceValueCallback(IsEnabledCoerceCallback)));
}
private static object IsEnabledCoerceCallback(DependencyObject d, object baseValue)
{
return (bool) baseValue;
}
}
Here, we are returning assigned value as it is from IsEnabledCoerceCallback. Before returning, you can also introduce the behavior : If user doesn't provide any value for IsEnabled, then use inherited value from parent, else use CButton.IsEnabled user assigned value.
On a side note, try setting null in place of new CoerceValueCallback(IsEnabledCoerceCallback) , see what happens.
I have two textbox.
Textbox A and Textbox B.
I want to bind these two text boxes to a single command button Button C.
That is if text of any the two text box is changed by the user then only the command button should get active.
Its really easy to achieve the above from Code Behind file but I was wondering that is it possible to bind a single control Button C to two elements Textbox A and Textbox B and achieve the needed through XAML.
Thanks and Regards.
If you want to enable the button if any of the two textboxes has text, you can use a MultiDataTrigger:
<TextBox x:Name="TextBoxA" />
<TextBox x:Name="TextBoxB" />
<Button x:Name="ButtonC">
<Button.Style>
<Style TargetType="Button">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Text, ElementName=TextBoxA}" Value=""/>
<Condition Binding="{Binding Text, ElementName=TextBoxB}" Value=""/>
</MultiDataTrigger.Conditions>
<Setter Property="IsEnabled" Value="False" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
My Suggestion is as follows:
Have TextBoxA bind to Field1 and TextBoxB bind to Field2 and
bind the Command of ButtonC to a relay command. Ensure that you implement the CanExecuteMethod
Articles here: MSDN Article on RelayEvents and here: Implementation of RelayCommand
In the canExecute method have an implementation that looks something like this:
public bool CanExecuteButtonC(object a)
{
If (!string.IsNullOrEmpty(Field1) && !string.IsNullorEmpty(Field2))
return true;
return false;
}
if the canExecute method returns false, the button will automatically be disabled, and if returns true it will be activated.
bind IsEnabled of button to a boolean property
code is not compiled or tested.
public bool IsButtonEnabled
{
get
{
return !String.IsNullorEmpty(String1) && !String.IsNullorEmpty(String2);
}
}
Make sure propertychanged for IsButtonEnabled is fired when the strings are changed
public string String1
{
//get should be here
set
{
_string1 = value;
OnPropertyChanged("IsButtonEnabled");
OnPropertyChanged("String1");
}
}
I have a strange problem, I'm currently trying to styling a SingleUpDown Control (from Extended WPF Toolkit)
This is the current style that does not work:
<xctk:SingleUpDown>
<xctk:SingleUpDown.Style>
<Style TargetType="xctk:SingleUpDown">
<Style.Triggers>
<DataTrigger Binding="{Binding _ThresoldLocked}" Value="True">
<Setter Property="Value" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</xctk:SingleUpDown.Style>
</xctk:SingleUpDown>
While these two examples work like a charm:
<xctk:SingleUpDown>
<xctk:SingleUpDown.Style>
<Style TargetType="xctk:SingleUpDown">
<Style.Triggers>
<DataTrigger Binding="{Binding _ThresoldLocked}" Value="True">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
</Style.Triggers>
</Style>
</xctk:SingleUpDown.Style>
</xctk:SingleUpDown>
Or
<xctk:SingleUpDown>
<xctk:SingleUpDown.Style>
<Style TargetType="xctk:SingleUpDown">
<Setter Property="Value" Value="2"/>
</Style>
</xctk:SingleUpDown.Style>
</xctk:SingleUpDown>
What I'm doing wrong with the first Style?
There's no Error in the Output window..
EDIT:
This is my minimalist example:
http://i.stack.imgur.com/EzxBP.png
Usually when it comes to styles and triggers people are not aware of precedence, namely that local values among others completely override styles. Given the fact that you did not set any properties on the controls all styles should work.
So for example if you set the Value on your control (<xctk:SingleUpDown Value="0">...) the trigger does nothing, you would then move defaults into a Setter.
<xctk:SingleUpDown>
<xctk:SingleUpDown.Style>
<Style TargetType="xctk:SingleUpDown">
<Setter Property="Value" Value="<default here>"/>
<Style.Triggers>
<DataTrigger Binding="{Binding _ThresoldLocked}" Value="True">
<Setter Property="Value" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</xctk:SingleUpDown.Style>
</xctk:SingleUpDown>
Edit:
I made an observation about the control's behaviour. If you have a setup as shown above the Trigger will work, so the default setter is kind of necessary here.
Also you should note that manipulating the value will probably set it locally, thus overriding any further effects from the trigger. You can get around this by using animations instead of triggers as they have the highest precedence, however this may then void any manual manipulation.
Maybe you should move your logic away from the UI and just bind the Value directly to a property on your view-model/model, e.g.
private bool _ThresholdLocked;
public bool ThresholdLocked
{
get { return _ThresholdLocked; }
set
{
if (value != _ThresholdLocked)
{
_ThresholdLocked= value;
OnPropertyChanged("ThresholdLocked");
OnPropertyChanged("Value"); //Value is also affected
}
}
}
private float _Value;
public float Value
{
get
{
if (ThresholdLocked)
return 2.0f;
return _Value;
}
set
{
if (value != _Value)
{
_Value = value;
OnPropertyChanged("Value");
}
}
}
I using Extended WPF Toolkit, but I don't have SingleUpDown there, but you probably talking about different version than this is: http://wpftoolkit.codeplex.com/. I have there DoubleUpDown and your nonfunctional style works for me.
I am fairly new to WPF but have spent time researching WPF validation, and have not yet seen a good approach to conditional validation.
To simplify the situation greatly, let's say I have two textboxes and a submit button. The user enters a string in the first textbox. If the user enters, for example "ABC", then the second textbox should be a required field (I'd want the background to be a light blue color, to signify this), and the submit button should be disabled until that textbox is populated.
How can this be done? Is there an easy way to add/remove validations in runtime? 'DataAnnotations' (http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx) seemed like a good starting place, however I can't mark a field with the [Required] attribute, as the field won't always be required. Basically, I need something like 'Required if Field1 = 'ABC'
Thanks!
I would handle it using MVVM and here is a sample for that.
Implement IDataError Info on the class and that will implement two properties Error and this[string columnName] you can implement the second property with your binding errors that you want
public class MainViewModel:ViewModelBase,IDataErrorInfo
{
public string Error
{
}
public string this[string columnName]
{
get
{
string msg=nulll;
switch(columnName)
{
case "MyProperty": //that will be your binding property
//choose your validation logic
if(MyProperty==0||MyProperty==null)
msg="My Property is required";
break;
}
return msg;
}
}
Also Set ValidateOnErrors=True in binding of a textbox. here ColumnName is the name of the property that is changed and that has ValidateOnErrors set to true. Check here and put up the conditions and return message then you will see the errors on the tooltip when you put this style in your Resources.
<UserControl.Resources>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true" >
<Setter Property="Foreground" Value="Red"/>
<Setter Property="Background" Value="MistyRose"/>
<Setter Property="BorderBrush" Value="Red"/>
<Setter Property="BorderThickness" Value="1.0"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self},Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
and here is a sample of the textbox
<TextBox Text="{Binding UpdateSourceTrigger=PropertyChanged, Mode=TwoWay,
Path=PropertyName,ValidatesOnDataErrors=True}" Name="textBox1">
<Validation.ErrorTemplate>
<ControlTemplate>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
I would just handle this logic in your ViewModel (assuming you're using an MVVM pattern, if not just in your code-behind).
Fire some logic on the TextChanged event for the first textbox that ultimately sets the appropriate properties. Essentially I'm saying code this validation manually. Once you start getting into more complex validation logic like this your going to start running into the limitations of the validation frameworks / declarative validation.
I have a very simple object called CellData. Is defined as:
public sealed class CellData
{
internal string DisplayText
{
get;
set;
}
public string Color
{
get;
set;
}
public override string ToString()
{
return this.DisplayText;
}
}
I can get it to display using the WPF toolkit DataGrid just fine. However, I want to be able to change the background color of each cell based on what data is in the cell. I'm having trouble understanding what type of binding I need to do because I can't see to get to the CellData object in my DataTrigger. I have tried the following, and several other variations but I can't get it to work:
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=(CellData).Color, Mode=OneWay}" Value="1">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
I am pretty new to XAML databidinding so any suggestions would be greatly appreciated. Thank you.
I'm guessing you have a RowData object that contains several CellData objects, that you have bound the ItemsSource of the DataGrid to a list of RowData objects, and that you are using DataGridTextColumns or other DataGridBoundColumns bound to the properties on RowData, maybe just by using AutoGenerateColumns="True".
The problem is that the DataContext for the cell is actually the RowData, not the CellData. The Binding is only used for the Text property of the TextBlock and TextBox. This is useful if you want to have the triggers based on other properties of the RowData object, but makes it difficult in scenarios like yours where you have a rich data structure for the cell data.
If you are creating the columns explicitly, you can just use the property for the column again in the trigger:
<DataGridTextColumn Binding="{Binding Foo}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Style.Triggers>
<!-- DataContext is the row, so start
binding path with Foo property -->
<DataTrigger Binding="{Binding Foo.Color}" Value="1">
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
Unfortunately, this won't let you share the style between columns since it is specific to the column name. If you want to do that, you may need to create a custom DataGridColumn subclass that has a property of type Binding that it applies to the DataContext property of the object returned by GenerateElement and GenerateEditingElement.
(Using a Binding with RelativeSource of Self as you did in your sample gives you the element in the visual tree rather than its DataContext, which won't help you get to the CellData object.)
You can use a ValueConverter: create a binding between the cell's background color and the Color property of your object, then add a ValueConverter to ensure that your data is properly converter to the object needed to set the background.