Modify Style (add a trigger) dynamically on runtime - c#

I want to add some graphic items on runtime in my object of my WPF application. My goal is to add a circle around my button (only by codebehind on runtime) and to add a trigger to my ControlTemplate.
Everything is fine, my circle around my button shows up BUT my problem is: I cannot create any trigger, it says:
After a 'TriggerCollection' is in use (sealed), it cannot be modified.
What I understand is I cannot modify a Style when it's rendered or used by something else.
I tried to copy the Style to modify the new one but I can't access to the trigger.
My object is like this:
<Style x:Key="Button_1" TargetType="{x:Type MyDLL:MyObject}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type MyDLL:MyObject}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="190" Height="140">
....
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="Property1" Value="True">
<Setter.../>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If I copy my Style to a new style, how can I add the trigger into the ControlTemplate?
If I copy my ControlTemplate to a new ControlTemplate, the new one is also sealed.
My code behind to create my trigger is:
Trigger trigger = new Trigger { Property = DragModeProperty, Value = false };
Setter setter = new Setter { Property = VisibilityProperty, Value = Visibility.Collapsed, TargetName = "DRAG" };
trigger.Setters.Add(setter);
Template.Triggers.Add(trigger); // THIS ONE CAUSE THE ERROR

Found it.
I just create my trigger on a new container (Grid) instead of on my Button.
Simple.

Related

WPF Override Style Value

I'm using MaterialDesignInXaml for WPF which provides 3rd party controls and styles. I need to edit one of these styles by changing one property.
I am using an Expander control which has a template creating a bunch of child controls. I've discovered the child 'Border' control (4 layers deep) has the property (padding) which I need to set to zero.
See this output from Snoop showing the property I need to change:
Link to image
My question is how can I do this? I've tried extending the style used by the control as follows, but it isn't changing anything so I assume I'm doing something wrong?
<Style TargetType="{x:Type Expander}"
x:Key="MaterialDesignExpanderHeadless"
BasedOn="{StaticResource MaterialDesignExpander}">
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="Padding" Value="0"></Setter>
</Style>
</Style.Resources>
</Style>
I am able to use the style like this. And I know this is working for sure:
<Expander Header="Header Content" Style="{StaticResource MaterialDesignExpanderHeadless}">
Some Content
</Expander>
You're right, this method should work. Something else is setting the border's padding.
Snoop is telling you the padding is defined by the parent template, which could be the HeaderSite (ToggleButton).
You could try to extend the ToggleButton style (BasedOn) or redefine it locally.

WPF Image Control: Trigger stops working once Source is modified in code

I am experiencing some issues when changing the 'Source' property of a WPF Image control.
I have three image sources defined:
<Window.Resources>
<BitmapImage x:Key="eyeSelImage" UriSource="/Images/eye-Sel.png" />
<BitmapImage x:Key="eyeSelHlImage" UriSource="/Images/eye-SelHl.png" />
<BitmapImage x:Key="eyeDisabled" UriSource="/Images/eye-Disabled.png" />
</Window.Resources>
My Image definition looks like this:
<Image x:Name="testImage" Width="100" Height="100">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="{StaticResource eyeSelImage}"/>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Source" Value="{StaticResource eyeDisabled}"/>
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
It works as expected. If the image control is disabled, the image changes. Once it becomes enabled, it changes back. I tested this by making a button (btn_DisableEnable) which, when clicked, toggles the 'IsEnabled' property of the 'testImage'.
However as soon as I change the 'Source' of my 'testImage' image control in code, the 'IsEnabled' trigger seems to stop working. I made another button and in its 'Click' event handler I do the following:
BitmapImage tempImage = new BitmapImage();
tempImage.BeginInit();
tempImage.UriSource = new Uri("pack://application:,,,/testApp1;component/Images/eye-SelHl.png");
tempImage.EndInit();
testImage.Source = tempImage;
After pressing this button, the image's source properly changes to the 'eyeSelHlImage' resource. However the image no longer changes to its disabled representation and back when the 'btn_DisableEnable' is clicked.
What could be the problem? All help is greatly appreciated.
Thanks!
Since dependency properties can be set by different mechanisms (triggers, styles, themes, inheritance and so on), it has been defined a "setting" precedence list.
You can find it here.
As you can see local value - i.e. if you set the directly property with code or by XAML, or by using the SetValue method - has an higher precedence rather than styles or triggers.
So setting the Source property by code has precedence on what value your trigger may set.
This is the reason why, after you call your code, your trigger does not work any more.

Disable Style in WPF XAML?

Is there anyway to turn off a style programatically?
As an example, I have a style that is linked to all textboxes
<Style TargetType="{x:Type TextBox}">
I would like to add some code to actually stop the style elements being used, so basically reverting back to the default control style.
I need a way to make a switch in my styles, so I can switch between Windows default style and my custom style through C# code.
Is there anyway to do this?
Thanks
Working Solution
Switching between themes in WPF
For setting the style to default,
In XAMl use,
<TextBox Style="{x:Null}" />
In C# use,
myTextBox.Style = null;
If style needs to be set as null for multiple resources, see CodeNaked's response.
I feel, all the additional info should be in your question and not in the comments. Anyways, In code Behind I think this is what you are trying to achieve:
Style myStyle = (Style)Application.Current.Resources["myStyleName"];
public void SetDefaultStyle()
{
if(Application.Current.Resources.Contains(typeof(TextBox)))
Application.Current.Resources.Remove(typeof(TextBox));
Application.Current.Resources.Add(typeof(TextBox),
new Style() { TargetType = typeof(TextBox) });
}
public void SetCustomStyle()
{
if (Application.Current.Resources.Contains(typeof(TextBox)))
Application.Current.Resources.Remove(typeof(TextBox));
Application.Current.Resources.Add(typeof(TextBox),
myStyle);
}
You could inject a blank Style that would take precedence over your other Style. Like so:
<Window>
<Window.Resources>
<Style TargetType="TextBox">
<Setter Property="Background" Value="Red" />
</Style>
</Window.Resources>
<Grid>
<Grid.Resources>
<Style TargetType="TextBox" />
</Grid.Resources>
</Grid>
</Window>
In the example above, only the Grid's implicit Style would be applied to TextBoxes in the Grid. You could even add this to the Grid programmatically, something like:
this.grid.Resources.Add(typeof(TextBox), new Style() { TargetType = typeof(TextBox) });
I know the answer has been accepted, but i want to add my solution which works awesome in the following scenario:
One main application using mahapps.metro
additional project imported from the main application with no reference to mahapps.metro, it is imported as a plugin (loading compiled .dll on the fly)
using the < ToolBar> re-styles everything to null therefore the mahapps.metro styles are not being applied to items inside the toolbar.
usercontrol is used to provide custom controls to the main application.
in the user control root set the resources:
<UserControl.Resources>
<Style x:Key="ButtonStyle" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}" />
<Style x:Key="ComboBoxStyle" TargetType="ComboBox" BasedOn="{StaticResource {x:Type ComboBox}}" />
</UserControl.Resources>
then the toolbar code can be the following
<ToolBar>
Block Template:
<ComboBox Style="{StaticResource ComboBoxStyle}"/>
<Button Content="Generate!" Style="{StaticResource ButtonStyle}"/>
</ToolBar>
this successfully applies the main application style to the controls inside the < ToolBar>
In Xaml, you can override this by setting a style explicitly. In code-behind, you can also set the style explicitly.
<TextBox Style="{StaticResource SomeOtherStyle}"/>
myTextBox.Style = Application.Resources["SomeOtherStyle"];

how can i use a control in template?

<Style x:Key="abc" TargetType="{x:Type Window}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Window}">
<button x:name="btn">my button!!</button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
...
<Window ... Style="{StaticResource styleMainWindow}">
How can i use the button btn?
come up to your expectations,
MessageBox.Show(this.btn1.name);
was occured an error at compile time. and also btn1 didn't show up in intelisense.
Try the FindName method on the ControlTemplate class.
Assuming this is your Control's context:
var button = (Button)this.Template.FindName("btn", this);
I guess mjk6026, you have misunderstood WPF templates from actual member elements of a window.
It is true that when we name a UI element (x:Name) that is not part of any template, we can access that element by name in the code behind.
For templates you would have to use Template type's FindName() method.
So assuming that `this' means the window to which you have applied your style, the way you can access the button is (Button)this.Template.FindName("btn", this)
Let me know if this answers your question...

How to add a trigger to a WPF custom control without overriding the existing style?

I am creating a simple custom control extending from toggle button that allows the user to specify checked and unchecked content directly in XAML. It works well but it is based on a trigger, and I don't know how to define the trigger except in a style. If I define the style, then I lose anything set outside of the custom control.
What I would like to be able to do is just append this trigger to any existing style set elsewhere on the control.
Here's the XAML for the style/trigger.
<ToggleButton.Style>
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource Self}, Path=UncheckedContent}" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content"
Value="{Binding RelativeSource={RelativeSource Self}, Path=CheckedContent}" />
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
I tried inheriting the style via the BasedOn with a default type but it won't work if the custom control has an explicit style set by its parent. I also considered EventTriggers but I do not believe there would be an event to initialize the control.
Thanks for any help anyone can offer. :)
Just to clear things up on the terminology here: A user control is a control that derives from the UserControl class. If I understood you right you derived from ToggleButton to add the UncheckedContent and CheckedContent properties. In that case you have created a custom control. It's always easier to follow if we agree on common terminology :)
As far as I know you can not do such a generic style inheritance in XAML. You always have to specify explicitly what style a another style is based upon. Your style can either be based on the default style for ToggleButton or on a specific other style. If you can't build a style inheritance chain that respects that, this approach won't work.
But since you have a custom control, couldn't you write a default style for it that is based on the default toggle button style like this?
<Style TargetType="{x:Type CustomToggleButton}"
BasedOn="{StaticResource {x:Type ToggleButton}}">
Then whenever you apply an explicit style to a toggle button you would specify that it is based on the default toggle button style.
Also you could write a (default) control template for your new toggle button in Themes\Generic.xaml that contains the above triggers. In blend you can get a copy of the default template for toggle button ("Edit Template"->"Edit a Copy") so you can make sure that your toggle button looks exactly like the normal toggle button. Then incorporate the triggers above into that template.
BTW: you do not have to create a new control just to add new properties. You can add new properties to an existing control using attached properties. They can be used from XAML just like normal properties.

Categories

Resources