How to set an event function via a style? - c#

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.

Related

Hiding AutoSuggestBox in NavigationView

I have a NavigationView control with an AutoSearchBox displayed:
<NavigationView Style="{StaticResource CompactNavigationViewStyle}"
x:Name="NavigationView" OpenPaneLength="280"
VerticalAlignment="Stretch" VerticalContentAlignment="Stretch"
AlwaysShowHeader="False">
<NavigationView.AutoSuggestBox>
<AutoSuggestBox PlaceholderText="Search" QueryIcon="Find" Width="235"
x:Name="SearchTxt" QuerySubmitted="OnSearch" />
</NavigationView.AutoSuggestBox>
For some page I don't want to show the Search (i.e SettingPage), so on NavigationView ItemInvoked event I added this code:
private void NavigationView_ItemInvoked(NavigationView sender,
NavigationViewItemInvokedEventArgs args)
{
if (!args.IsSettingsInvoked)
{
string tag = (args.InvokedItem as string);
switch(tag)
{
case "settingpage":
case "exportpage":
SearchTxt.Visibility = Visibility.Collapsed;
break;
default:
SearchTxt.Visibility = Visibility.Visible;
break;
}
// Code to load new page to Frame here
}
}
The AutoSuggestBox is hidden from NavigationView, but the Search icon still displayed on Compact mode, how to hide this icon too?
Unfortunately, what you are looking for does not appear to be possible with the NavigationView control. This is relatively new control, however, so Microsoft may be updating it in the future.
For the technical reason, setting a value to the AutoSuggestBox property does more than just add an AutoSuggestBox to the NavigationView; it also changes some other internals of the NavigationView (such as the one specifying showing that search icon when the pane is collapsed).
The AutoSuggestBox property is an optional property, and designed to contain a search box "to allow for app-level search". This suggests that it is designed to be always visible across the entire app when present (although I could see a reasonable argument being made for having it disabled on certain pages). But simply, it looks like this is a use case the control is not designed for.
As for some ideas for work-arounds:
Option 1
The one place you can freely put whatever content you want is the NavigationView.Footer. You can implement something like the above code, and then adjust the StackPanel's visibility property.
The main downside to this option, of course, is that the footer is stuck at the bottom, which may be an odd place to put a search bar. You also will need to give the StackPanel some visual styling to mimic the hover and click effects on the rest of the NavigationView.
<StackPanel Orientation="Horizontal"
Margin="10">
<TextBlock Style="{StaticResource HamburgerMenuIconStyle}"
Text=""></TextBlock>
<TextBlock Style="{StaticResource HamburgerMenuItemTextStyle}"
Text="Home"></TextBlock>
</StackPanel>
And then the supporting styles for the Page.Resources, so that the above bindings work (you may need to play around with the numbers for the margins and font sizes, but this looked good for me):
<Page.Resources>
<Style x:Key="HamburgerMenuIconStyle" TargetType="TextBlock">
<Setter Property="FontFamily" Value="Segoe MDL2 Assets"></Setter>
<Setter Property="FontSize" Value="18"></Setter>
<Setter Property="Margin" Value="5,0,0,0"></Setter>
</Style>
<Style x:Key="HamburgerMenuItemTextStyle" TargetType="TextBlock">
<Setter Property="VerticalAlignment" Value="Center"></Setter>
<Setter Property="Margin" Value="15,0,20,0"></Setter>
<Setter Property="FontSize" Value="16"></Setter>
</Style>
</Page.Resources>
Option 2
You can implement your own NavigationView, using a SplitView. This is actually not too difficult, and there is even a fantastic tutorial in the following link (which I have used for several personal projects): https://mva.microsoft.com/en-US/training-courses/windows-10-development-for-absolute-beginners-14541?l=4DLgEZ0qB_5705244527. Specifically see Video #22, if the link doesn't open that one.
Implementing your own works well enough for visual looks, and for navigational functionality. Unfortunately, however, the more advanced features in the built-in NavigationView control are considerably more time-consuming to implement, such as the built-in "Back" navigation support, and the built-in fluent design styling. You can wrap this into a UserControl or a custom Control, so that you can reuse it in other projects, but the initial time investment will still be high.
It is worth noting that the requirements you are imposing on your users by using a NavigationView in the first place should be considered (if you have not done so already):
The NavigationView was introduced in the Fall Creator's Update, so any users must have a version newer than that. Somewhere around 90% of users who have Windows 10 do, so this is pretty safe for most people.
The Back navigation functionality of the NavigationView was introduced even more recently, specifically in v10.0.17110.0. This is still in Windows Insider, and not generally released yet (I believe), so this functionality specifically may not be a good choice for a larger audience yet. The reason I point this out, is that to reach a larger audience, you would need to implement the back functionality yourself anyway, so the barrier to writing your own NavigationView may not be as high as it seems.
Hope that helps!
<NavigationViewItem
x:Name="NaviSearchItem"
Icon="Find"
Visibility="Collapsed" />
<NavigationViewItem x:Name="NaviSearchBarItem">
<AutoSuggestBox
x:Name="NaviSearchBar"
PlaceholderText="Search"
QueryIcon="Find" />
</NavigationViewItem>
You can surround the AutoSuggestBox with NavigationViewItem and place the Search Button above it. Then you need to set the visibility of NaviSearchBarItem instead of the AutoSuggestBox. This hides the AutoSuggestBox perfectly.
However, there is a tiny issue with this solution. When you click on the NaviSearchBarItem to open the Pane, the animation of tab (a blue pipe that indicates the selected item) will still slide to the NaviSearchBarItem, and then it disappears. The ideal solution should be that the blue indicator still remain on the original item. I don't know how to fix this.

How to set a binding inside a style?

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.

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 Template Binding with more than 1 content

So, I have this Window. On it, I'm creating a list of TextBlocks and TextBoxes in pairs. When you click on either, they will put a number in the corresponding TextBox, and set some values in the background. This all works well now.
I have the following XAML to create a custom Checkbox (as it has the behavior I'd like to use for this). My problem is that I want to bind different content into both the TextBlock and TextBox. For the TextBlock, I bound to the Content property, but I can't find a suitable option to satisfy the second binding. I considered placing it in the tag, but this didn't feel right, and in any case, I'm already binding an index value I require into there.
<Style x:Key="CustomCHK" TargetType="{x:Type CheckBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<DockPanel LastChildFill="True">
<TextBox DockPanel.Dock="Right" Width="50" Height="30" />
<TextBlock Text="{TemplateBinding Content}" TextWrapping="Wrap" />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Feels like there should a simple solution to this, but I'm just trying to decide what's best. Do I create a custom checkbox class and just add a couple properties?
As always, I appreciate any direction you can offer me.
Unfortunately, there is no straightforward way to do this. I can see just two (somewhat flawed) workarounds:
Subclass CheckBox to suit your needs, and add the additional content properties that you need. The advantage is that this will fully enable your IDE's programming help for setting the content properties, and those properties will be typesafe. The downside is that your will need to add C# code for the sole purpose of declaring the additional content properties (i.e. without adding any "behavioral logic"), which somehow seems to conflict with a "clean" XAML-only for presentation approach.
You could try passing an array to the Content property, and then place several ContentPresenter instances in your control template, each of which bind to another item in the array. Binding property paths should support indexed access, though your code may become a bit verbose, as arrays in XAML have to be written explicitly by using the x:Array markup extension.

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.

Categories

Resources