How to set a binding inside a style? - c#

I am fairly new to Xaml.
I am learning UWP(Universal Windows Platform) and I have more buttons and I want to bind their Background property to a property of a ViewModel that will change during some events.
I implemented the INotifyPropertyChanged and everything works ok (the color of the buttons change) when I bind the Background property in the Buttons' declaration in XAML:
<Button Content="0" Grid.Column="0" Grid.Row="5"
Background="{Binding ButtonColor, Source={StaticResource AppViewModel}}" Style="{StaticResource BasicButton}"/>
StaticResource AppViewModel is a resource in App.xaml:
<Application.Resources>
<viewModel:AppViewModel x:Key="AppViewModel" />
</Application.Resources>
I don't know how ok is to declare a ViewModel for App.xaml, but it's a solution I found for having global variables (the variables are held inside the viewModel).
Now back to my question:
As I don't want to bind the Background on every single button, I tried to add it on the style like this:
<Style x:Key="BasicButton" TargetType="Button">
<Setter Property="Background" Value="{Binding ButtonColor, Source={StaticResource AppViewModel}}" />
</Style>
But now when the color variable is changing during running the app, the UI doesn't update anymore.
It seems that binded properties in styles don't respond to changes of variables.
What am I doing wrong?
Thank you for any answers.

After more searching I found a video from Jerry Nixon : http://blog.jerrynixon.com/2013/01/walkthrough-dynamically-skinning-your.html
It seems that because we don't have DynamicResource in uwp / winrt, we have to do a trick:
We renavigate to the same frame. So after we change the property, we have to do something like this:
var frame = Window.Current.Content as Frame;
frame.Navigate(frame.Content.GetType());
frame.GoBack();
It's like invalidating a control in Windows Forms. It's making the UI redraw itself.
I'm not sure if this has side effects, I'll have to dig more. I'll come back if I find any.

Related

Materialize Design in XAML - does not work for custom XAML controls

I have the following custom control that inherits from a TextBox:
class MyTextBox : TextBox {
//custom properties, events and functions
}
It is used like so..
<TextBox Width="100" Height="30" Text="TextBox" Margin="20" />
<local:MyTextBox Width="100" Height="30" Text="12345" Margin="20" />
I have used the Material Design In XAML Toolkit in my project, which is working good, except that MyTextBox does not have that "Material Effect" with it as seen in the below screenshot.
What should I do to give that material design style in MyTextBox?
Styles are applied through the property TargetType. When create a new type, it's not getting picked up as a TextBox to have the MaterialDesignTextBox style applied to it.
You must have a style, likely created automatically by VisualStudio in your Generic.xaml for your MyTextBox. You need to change it to:
<Style TargetType="{x:Type controls:MyTextBox}" BasedOn="{StaticResource MaterialDesignTextBox}"/>
So WPF knows to apply the style to MyTextBox types as well as TextBox
Try applying the textbox, the property borderthickness (0,0,0,2) and you get that same effect of material design!

Accessing elements in a xaml defined style

I have followed the DiagramDesigner example on Codeproject for learning how to use Adorners in WPF as it fits quite a few of my needs relatively closely.
I have adapted the implementation a little, and also added my own adorner, for controlling the opacity of a control via a slider (slider on the adorner).
Following the same methods as the author, I placed the slider and other feature in a xaml style definition file as below. I am just now struggling A) to figure out how to access the slider at any level, B) how best to start hooking this up with an underlying Viewmodel that will be used for various settings (on adorners).
<Style x:Key="OpacityAdorner" TargetType="{x:Type adorners:OpacityChrome}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type adorners:OpacityChrome}">
<Grid>
<Slider x:Name="OpacitySlider" Style="{StaticResource OpacityControl}" ToolTip="Alter the opacity of the image to overlay with other images" Visibility="Collapsed"/>
<Ellipse x:Name="OpacitySliderEnable" Style="{StaticResource OpacityIcon}" ToolTip="Alter the visual opacity of the image" Visibility="Visible"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The codeproject example is here http://www.codeproject.com/Articles/22952/WPF-Diagram-Designer-Part
A) Use something like the following snippet to get the slider from the applied template.
var slider = opacityAdorner.Template.FindName("OpacitySlider", opacityAdorner) as Slider;
there are cases where the template has not yet been applied, in that case you need to preceed the previous call with the following:
opacityAdorner.ApplyTemplate();
B) The best approach for hooking up with the view model (in my opinion) is to expose the required properties as dependency properties on the OpacityChrome adorner. You then use normal Binding to hook up the new properties to the view-model, and TemplateBinding to hook them up to the template elements.

WPF - Focus Control Within DataTemplate applied to FlyoutControl

I have a DataTemplate which contains a TextBox. The DataTemplate is bound to the ContentTemplate property of a Style for the DevExpress FlyoutControl. The Flyout Control itself is within the ControlTemplate of another TextBox.
When the TextBox with the FlyoutControl is focused, I want to redirect focus to the first TextBox in the FlyoutControl's ContentTemplate (from the DataTemplate). Setting FocusManager.FocusedElement={Binding RelativeSource={RelativeSource Self}} on the TextBox I want focused accomplishes this the first time, but once the Flyout has loaded it no longer works.
I have tried every suggestion I can find and nothing so far has worked. I can get the TextBox I want to reference in code and call Focus(), but it always returns false. At best, when I try to focus it in code, the Flyout is focused instead, but never the TextBox within the Flyout.
Here is what each relevant part looks like (irrelevant code omitted):
<DataTemplate x:Key="FlyoutTemplate">
<Grid>
<dxe:TextEdit x:Name="TextThatWantsFocus"
FocusManager.FocusedElement={Binding RelativeSource={RelativeSource Self}}" />
</Grid>
</DataTemplate>
...
<Style x:Key="FlyoutStyle" TargetType="dxe:FlyoutControl">
<Setter Property="ContentTemplate" Value="{StaticResource FlyoutTemplate}"/>
</Style>
...
<dxe:TextEdit>
<dxe:TextEdit.Template>
<ControlTemplate>
<StackPanel>
<dxe:TextEdit x:Name="InnerTextEdit" />
<dxe:FlyoutControl Style="{StaticResource FlyoutStyle}"/>
</StackPanel>
</ControlTemplate>
</dxe:TextEdit.Template>
</dxe:TextEdit>
The flyout is being opened in code. It is here that I also would like to focus the TextBox (TextThatWantsFocus). However, nothing I have tried will give it focus (except for FocusManager handling it the first time), including the typical SO answer involving triggers. Any ideas would be greatly appreciated.
I took DmitryG's advice and submitted a DevExpress support ticket, and they were able to provide a solution.
The issue was resolved by handling the Loaded event of the TextEdit I want focused and using the dispatcher to focus it:
private void TextThatWantsFocus_Loaded(object obj, RoutedEventArgs e)
{
var text = obj as FrameworkElement;
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(delegate()
{ text.Focus(); }));
}
I suggest you using the FocusBehavior from DevExpress MVVM Framework:
<DataTemplate x:Key="FlyoutTemplate">
<Grid>
<dxe:TextEdit>
<dxmvvm:Interaction.Behaviors>
<local:FocusBehavior/>
</dxmvvm:Interaction.Behaviors>
</dxe:TextEdit>
</Grid>
</DataTemplate>

Template for basic reusable button

In WPF (VS2013), I'm creating a button like so:
<Button>
<Label>1</Label>
</Button>
Each of these buttons will have more to it, such as increased font size of the Label, grid row/column assignment, and I might use a binding for the label so that I can change the number. I'm creating a calculator app so I need to reuse this button 10 times (one for each number 0-9). Instead of copying/pasting this button XML 10 times, I wanted to see if I could templatize it.
I've read a little about ControlTemplate and DataTemplate, but I'm not sure if either of these are the correct thing to use. It's also not clear to me what should be a style or what should be a template.
So if someone could help me understand how to "templatize" the button and its styles (e.g. width, height, font size, etc) so that they can be easily reused, that would help a ton. Guidance is appreciated!
Use a ControlTemplate when you want to overwrite the entire template for a control, use a DataTemplate when you want to tell WPF how to draw a data object (usually the DataContext), and use ContentTemplate when you want to tell WPF how to draw the Content property of an object.
Creating a whole new ControlTemplate is quite complex. To demonstrate, check out this MSDN example for an example ControlTemplate for a Button.
In your case, I would recommend creating a Style for your button with setters for common properties such as Height, Width, Font, etc. If you want to draw your button's Content property in a custom way without completely overwriting the button template, include a ContentTemplate style setter to tell WPF how to draw the Button.Content property.
<Button Style="{StaticResource CalculatorButton}" Content="1" />
and
<Style x:Key="CalculatorButton" TargetType="{x:Type Button}">
<Setter Property="Height" Value="50"/>
<Setter Property="Width" Value="50"/>
<Setter Property="FontSize" Value="14" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding }" FontFamily="Wingdings 3" FontWeight="Bold" FontSize="18" Foreground="Navy" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
The ControlTemplate defines how the button looks, the ContentTemplate defines how the Button.Content looks, and the DataTemplate used for the ContentTemplate is defining how the data object of "1" will be drawn.
You can start with a copy of the style of the button. Use Blend (part of VS) to create that: open the context menu of the button inside the object tree, then select "Edit template" (or similar, don't have an english version at hand), then "Copy of template" (or alike).
Now you may change properties (in designer or XAML). Every button that shall have this style needs to reference this new ressource.
You need to create a new Style of a button. Learning curve is not too steep, but the benefits are enormous. You can start learning about it here: http://msdn.microsoft.com/en-us/library/ms745683(v=vs.110).aspx
Long story short: Open your project with Blend, right-click on your button, "Edit Style", "Edit a copy". If you choose to define it in Application, you can reuse it among other pages (it will be then in you App.xaml file)
Once you have the base style, edit it as much as you need.

How to set an event function via a style?

I have several GUI control elements of which some are supposed to generate the same action (code-behind function call) on mouse interaction (MouseEnter, MouseLeave).
[edit] I am performing some non style related functionality in my event handlers.
Right now I'm using event attributes in each control:
<Button Name="Button" Content="Button 1"
MouseEnter="GeneralMouseEnter" MouseLeave="GeneralMouseLeave"
PreviewMouseDown="Button1_PreviewMouseDown" PreviewMouseUp="Button1_PreviewMouseUp" />
<Button Name="NotInteractingButton" Content="Button 2"
/><!-- this button has no MouseOver-effects -->
<ToggleButton Content="ToggleButton"
MouseEnter="GeneralMouseEnter" MouseLeave="GeneralMouseLeave" />
<!-- needs to use IsMouseDirectlyOver on the slider knob... -->
<Slider Name="HorizontalSlider"
MouseEnter="GeneralMouseEnter" MouseLeave="GeneralMouseLeave"
ValueChanged="Slider_ValueChanged" />
<Slider Name="VerticalSlider" Orientation="Vertical"
MouseEnter="GeneralMouseEnter" MouseLeave="GeneralMouseLeave"
ValueChanged="Slider_ValueChanged" />
Since many controls in this example are calling the same two functions "GeneralMouseEnter" and "GeneralMouseLeave", I'd like to be able to define a style or something similar to encapsulate that behavior.
[edit - clarification]
This is supposed to become a kind of plugin later on.
(Include code and XAML files to any GUI program and set a style on each interactive control element...)
From what I found on the web, I can use EventTriggers like in this example:
<Style.Triggers>
<EventTrigger RoutedEvent="Click">
<EventTrigger.Actions>
<BeginAction TargetName="SomeAction" />
</EventTrigger.Actions>
</EventTrigger>
</Style.Triggers>
I don't know though if and how to call functions within an action.
Is it possible to get this functionality by creating a style with action + trigger to be applied to each control? How to do that?
How do I assign multiple styles (for multiple interaction events) to one control?
Is there maybe even a cleaner way to achieve this behavior?
[edit]
What if I want to, let's say, have mouse interaction on all sliders in my GUI?
Martin,
you can assign an event handler directly from a style using an EventSetter:
<Style TargetType="{x:Type Button}">
<EventSetter Event="Click" Handler="SomeAction"/>
</Style>
#ColinE:
I am not sure that using a style to perform event wire-up is a good idea. Styles, by definition, define the visual appearance of controls.
Unfortunately, this seems to be a common and widespread misconception about WPF styles: Although their name suggests they are, like what you say, merely meant to define the visual appearance, they are actually much more: It is helpful to view styles more generally as a shortcut for assigning a set of properties to a control.

Categories

Resources