Delegating the clicked event to the templated control - wpf

Since the CheckBox control doesn't increase the checkbox when the font size is increased, I decided to create my own variation of it (since it's to be used on a touch screen).
I have the following template:
<ControlTemplate x:Key="YesNoCheckbox" TargetType="{x:Type CheckBox}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Width="100" Name="myButton"/>
<ContentPresenter Grid.Column="1" Margin="4,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" TargetName="myButton" Value="Ja"/>
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Content" TargetName="myButton" Value="Nei"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
However, I need to delegate the click event from the button to the checkbox, so that the checked state is checked/unchecked.
I'm a total newbie, and this is probably very easy, but I fail to search this information up, probably due to a lack of correct keywords to search for.
I'm looking forward to hearing from you.
Thanks,
Stefan

CheckBox is a ToggleButton by itself and you are trying to place another button inside, which is wrong.
CheckBox changes its IsChecked automatically when it gets clicked. So you don't need to define another button inside. What you need to do instead is define a visual drawing in the CheckBox template that will scale when the FontSize increases and that will reflect current CheckBox state. Basically, you need to modify default style.

Related

Issues with XAML MaterialDesign TextBox style: background when enabled and helpertext

I'm using Material Design library in XAML. I need to re-style some components because I'm using a particular textbox in which I have a clickable icon.
<StackPanel Orientation="Horizontal" Width="328" Grid.Column="0" Background="#3B3A3A">
<TextBox Style="{StaticResource Style}" materialDesign:HintAssist.Hint="Text" Width="300"
TextWrapping="Wrap" materialDesign:HintAssist.HelperText="Text1"/>
<Button Opacity="1" Padding="2,0,0,0"
Height="53.2"
Background="Transparent"
BorderBrush="Transparent"
Command="{x:Static materialDesign:DialogHost.OpenDialogCommand}"
CommandTarget="{Binding ElementName=DialogSelection}">
<materialDesign:PackIcon Kind="ArrowExpand"/>
</Button>
First of all I need to remove the background when the textbox isFocused, so in the style I did something like this:
<Style x:Key="Style" TargetType="TextBox" BasedOn="{StaticResource MaterialDesignFilledTextFieldTextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Background="{TemplateBinding Background}"
Width="328"
materialDesign:BottomDashedLineAdorner.Thickness="{TemplateBinding Margin}">
<TextBox x:Name="text" TextWrapping="WrapWithOverflow" FontSize="16" FontWeight="Regular" Foreground="White"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#656565"/>
<Setter Property="Background" Value="#3b3a3a"/>
</Trigger>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#00B5CE"/>
<Setter Property="Background" Value="#656565"/>
<Setter Property="BorderThickness" Value="2"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The issue is that using this code for the style (with the control template) I don't have available the helper text. How can I combine the two things? (I mean my settings for the background when the textbox isFocused and a HelperText available?)
NB: If I don't modify the template and just setting the background it happens that the background when the textbox isFocused isn't as I want but the HelperText is visible
It does not work, because you overwrite the control template of the TextBox and omit most of it that is needed for it to work correctly. A control template defines the visual apprearance of a control, as well as the states and transitions between them. You cannot base a contol template on another as you can with styles. Creating a custom control template means:
Creating it from scratch with all the required states and parts or
Copying the base/default style and adapting it.
For standard WPF controls, you can find required parts and states in the documentation, e.g. for TextBox here. Omitting any of the required components will lead to unexpected behavior or broken visuals states. In case of Material Design, there are even more things necessary to make the controls work correctly, but they are not documented like on MSDN, so you have to create a copy of the default style and adapt it.
You can find the default styles for TextBox here on Github. In newer versions, the styles are renamed to MaterialDesignFilledTextBox because of this issue. The derived style hierarchy is as follows:
MaterialDesignTextBoxBase
MaterialDesignFloatingHintTextBox
MaterialDesignFloatingHintTextBox
MaterialDesignFilledTextFieldTextBox
The control template is defined in MaterialDesignTextBoxBase, which is the base style for all other styles. What you have to do now is to copy the MaterialDesignTextBoxBase style, give it a name and adapt the states as in your provided code. You can merge the setters of the three derived styles above into your custom style. Then you will have a custom style that incorporates all necessary states and parts that will work as you expect it.
You can't "combine" ControlTemplates. You must define a template as a whole.
This means that you should copy the default template in MaterialDesignFilledTextFieldTextBox that includes the helper text and then modify it as per your requirements, i.e. by adding a IsFocused trigger to it.
I am afraid you cannot base a template on another template or add triggers to a template that is defined elsewhere.

WPF Radio Button / MVVM Binding Issues

I've searched and discovered several proposed solutions, but I still can't make this work:
I'm using .Net 4.5, WPF, MVVM
I want to implement three radio buttons to allow a user to select which method (Primary, Secondary, Tertiary) to view. The UI, looks like this:
[ 1 ] [ 2 ] [ 3 ] (I had an image, here, but, apparently, I can't post that unless I have a 10 rep...)
My XAML:
<StackPanel Grid.Column="4" Orientation="Horizontal">
<RadioButton x:Name="rb1" Style="{StaticResource Radio1Button}" Command="{Binding Path=Select1ReceivingMethodCommand}" IsChecked="{Binding Path=ReceivingMethod1IsSelected}" />
<RadioButton x:Name="rb2" Style="{StaticResource Radio2Button}" Command="{Binding Path=Select2ReceivingMethodCommand}" IsChecked="{Binding Path=ReceivingMethod2IsSelected}" />
<RadioButton x:Name="rb3" Style="{StaticResource Radio3Button}" Command="{Binding Path=Select3ReceivingMethodCommand}" IsChecked="{Binding Path=ReceivingMethod3IsSelected}" />
</StackPanel>
Style for Radio1Button: (Others are similar, obviously)
<Style x:Key="Radio1Button" TargetType="{x:Type RadioButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Image Name="Button_Image" Source="/Resources/1.Normal.png" SnapsToDevicePixels="True" Width="20" Height="20" />
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="/Resources/1.Hover.png" TargetName="Button_Image" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Source" Value="/Resources/1.Disabled.png" TargetName="Button_Image" />
</Trigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Source" Value="/Resources/1.Pressed.png" TargetName="Button_Image" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My view model exposes bool properties for the state of each radio button, as indicated in the IsChecked propery for each. I have commands defined, as indicated, as well.
For some reason, the bool properties bound to the IsChecked property on each radio button never get called. (Neither getter nor setter). There are all sorts of other controls on the page and binding is working just dandy-like for them. The UI bits seem to work fine. The proper button images are shown for the state of the button and they function mutually exclusively, as expected. But there is no interaction with my view model.
I've tried pulling out the Command bindings, wondering if they were getting in the way, but no change. I've tried using converters, but the converter was never called.
I would also like a button to be disabled if there is no method assigned. (i.e. If only Primary and Secondary methods are defined, then the '3' radio button should be disabled. I was hoping the Command binding would help with that.)
What am I missing? What is the best way to implement this? (A controls other than radio buttons?)
it works on my computer, maybe you need to check your DataSource or see the output window from visual studio.
Never mind... There was a legacy DataContext assignment getting in the way. Once I cleaned that up, it worked as expected.
Sorry to bother... As you were.. Nothing to see here...

Constrain WPF element width inside its container

I am placing a templated Button with some content in inside it (both Image and TextBlocks) in some other control's Grid:
<SomeControl>
<Grid>
<SpecialButton/>
</Grid>
</SomeControl>
<Style TargetType="{x:Type local:SpecialButton}">>
...
<Setter Property="Template">
<Setter.Value>
...
<TextBlock/>
<TextBlock/>
</Setter.Value>
</Style>
The "SomeControl" control's width is dynamic - that is - it can be dynamically changed according to the screen width, it's container width, etc. Hence I use another method to calculate the "SpecialButton"'s width.
As long as the TextBlock within the button is not wider then the button itself The "SpecialButton" is being perfectly matching its size to it's "SomeControl" container.
However, if the TextBlock's text is longer then the width of the "SpecialButton", the "SpecialButton" right border disappears and the button looks really bad. That being said, the "SpecialButton"'s width is still constained to its containers' width although drawing is not being well calculated.
I am trying to find a way to constain the TextBlock's (or even better the complete Grid's width) without defining an absoulte width, so the "SpecialButton" will still get drawn well and ignore it's children's overflow. Something like CSS's {overflow:hidden} would have been great...
Note:
I can't allow wrapping since the "SpecialButton" height is also constained (and manually calculated).
Width (and height) in WPF elements is pretty straightforward once you get used to it. Your grid, by default is set to Auto width, which means it will request 100% of the width of the parent.
Instead of setting a maximum size, I generally put items that I want dynamically sized and positioned in a grid and set grid row and column sizes. So if I want something to take up 1/3 of the available horizontal space, I can have a grid with two columns, one with width set to "1*" and the other set to "2*" (2/3 being twice the size of 1/3), and put my item in column 1 set to take up all available width.
In your case, the text needs to be truncated as it is doing its best to render all of the text, and is extending the button past the bounds of it's container to do it. There are two options for the built in trimming capability. TextTrimming="CharacterEllipsis" and TextTrimming="WordEllipsis" Just put that in your textblocks. I would put the textblocks in a container of some kind to keep them separate. If the first has content that is long enough, it may push the other completely off the side.
Element Sizing
<Window
x:Class="MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" DataContext="{Binding}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<local:SomeControl Margin="8,8,8,0"/>
</Grid>
</Window>
Text Trimming
<ControlTemplate x:Key="ButtonBaseControlTemplate1" TargetType="{x:Type ButtonBase}">
<Microsoft_Windows_Themes:ButtonChrome x:Name="Chrome" BorderBrush="{TemplateBinding BorderBrush}" Fill="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderPressed="{TemplateBinding IsPressed}" RenderDefaulted="{TemplateBinding Button.IsDefaulted}" SnapsToDevicePixels="True" ThemeColor="Metallic">
<TextBlock Text="{TemplateBinding Content}" TextTrimming="WordEllipsis" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Microsoft_Windows_Themes:ButtonChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="RenderDefaulted" TargetName="Chrome" Value="True"/>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter Property="RenderPressed" TargetName="Chrome" Value="True"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Additionally, you might want to set the tooltip of your textboxes to the value of the text also. If the text is truncated and a user want to see what it is, they can mouse over the button and find out.

using control templates that vary only slightly

i have the following control template:
<ControlTemplate x:Key="GrayButton" TargetType="{x:Type Button}">
<Grid>
<Image x:Name="GrayButtonImage" Source="/Server;component/Images/bg.bmp"/>
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" Text="{x:Static props:Resources.IDS_ABORT}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter TargetName="GrayButtonImage" Property="Source" Value="/Server;component/Images/GrayButtonOn.bmp"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="GrayButtonImage" Property="Source" Value="/Server;component/Images/GrayButton.bmp"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
and here's one button using the control template:
<Button Height="40" HorizontalAlignment="Left" Margin="250,334,0,0" Name="ejf" VerticalAlignment="Top" Width="106" Template="{StaticResource GrayButton}" Click="execJournalPrgm" IsEnabled="False"/>
I need roughly 4-8 more buttons that vary only by text name/color... what is the best way to do this without repeating the control template definition 4-8 times?
any help would be greatly appreciated.
One way to do this is by making a custom control (by inheriting from Button) and setting up all the necessary properties which should be bound in the default template. Then you only need to create instances of that control and set those properties instead of changing anything in the template.

How to Add Modal Box Like Waiting Cursor on WPF App

In my WPF App, there are certain time consuming actions which are activated when the user clicks a button. What I would like to do is, show the User a Modal Box like Waiting Cursor, much like its done one Web Apps, to indicate that process is going on in the background. What kind of Controls do I have to achieve this?
Check out the Busy Indicator
You're not looking at much here. I'm not sure how you've designed your application, but for myself, I've created a class NavPage that extends the UserControl class and has a property Modal of type NavPage and a Close event.
So basically my NavPage control allows me to host other NavPag controls as modals within it. The Close function just closes the Page.
Here's how my template looks for the NavPage control.
<ControlTemplate x:Key="Fx_NavPage_ControlTemplate" TargetType="{x:Type fx:NavPage}">
<Grid>
<Border x:Name="TheContainer" CornerRadius="{TemplateBinding CornerRadius}" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}">
<ContentControl Content="{TemplateBinding Content}" />
</Border>
<Border x:Name="TheCover" CornerRadius="{TemplateBinding CornerRadius}" Background="#20000000" Visibility="Collapsed" />
<ContentControl x:Name="TheModal" Content="{TemplateBinding Modal}" Visibility="Collapsed" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Modal, RelativeSource={x:Static RelativeSource.Self}, Converter={StaticResource IsNullConverter}}" Value="False">
<Setter TargetName="TheCover" Property="Visibility" Value="Visible" />
<Setter TargetName="TheModal" Property="Visibility" Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
So basically I just have a Grid with a ContentControl (TheContainer), Border (TheCover) and another ContentControl (TheModal). Then I have a DataTrigger which checks if the value of the Modal property on the NavPage, and if its anything but null, it changes the visibility of TheCover and TheModal to visible. The cover is just a semitransparent Border control that stops the user from clicking controls in the main NavPage.
Hope this helps you out, or you could quite simply just use the BusyIndicator someone suggested above, I've never used it myself so can't give you any input there. If you decide to go this way and need help, let me now I can get more code for you.

Resources