XAML user control / window sizing - c#

I've created a basic framework that lets me display views within a modal dialog. The "container" window looks something like this (just the XAML pertinent to my problem):-
<Window SizeToContent="WidthAndHeight"
ResizeMode="{Binding DialogResizeMode}">
<DockPanel LastChildFill="True">
<Border DockPanel.Dock="Bottom">
<!-- This is where the buttons are rendered -->
</Border>
<ContentControl />
</DockPanel>
</Window>
Views (UserControls) are placed in the ContentControl, with a typical view looking something like this:-
<UserControl ..the usual stuff..
Width="500"
Height="400">
<!-- etc. -->
</UserControl>
By setting the width and height, it means the view can effectively control the dimensions of the modal dialog (due to the container window's SizeToContent="WidthAndHeight" setting).
The problem is, I now need to make the dialog resizable in some scenarios. If I set the parent Window's ResizeMode to CanResize, the user control remains at its fixed size in the centre of the dialog as the window is resized.
I suspect I'll need to use a different approach to setting the initial window size, such as providing the width and height for the window to bind to (unless I'm missing a simple solution). Any suggestions?
Edit
I've found a solution by removing the SizeToContent option from the parent window, and removing the Width & Height from the user control XAMl. I now set the window size from the user control's Loaded event:-
var window = Window.GetWindow(this);
window.Width = 500;
window.Height = 400;
It feels a bit hacky though - it would be nice if this could be accomplished using a XAML-only solution.

What is preventing you from binding to your ResizeMode property, and using Style triggers to set the properties? Something along the lines of,
<Style TargetType="{x:Type UserControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ResizeMode, RelatveSource={RelativeSource AncestorType=Window}}" Value="NoResize">
<Setter Property="Width" Value="500"></Setter>
<Setter Property="Height" Value="400"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>

Related

Why does a TextBox not inherit the background colour?

Here is a fully reproducible example:
<Window x:Class="DemoWPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Name="GridMain">
<Grid.Resources>
<Style x:Key="{x:Type Grid}">
<Setter Property="Control.Background" Value="Red"/>
</Style>
</Grid.Resources>
<StackPanel>
<Label>First Content</Label>
<TextBox>First Edit</TextBox>
<StackPanel>
<Label>Second Content</Label>
<TextBox>Second Edit</TextBox>
</StackPanel>
</StackPanel>
</Grid>
</Window>
The output of this is as follows:
What I find confusing is that the TextBox control has a BackgroundProperty which inherits from Control - no different than the Label. However, as can be seen the TextBoxes do not have their background colour changed. Although the Grid does not have a Control.Background property, but has a Panel.Background property, yet it still has its background property set, even though the property being set is Control.Background.
Short answer:
Because it's not transparent, it has a color by default:
A text box is an input field so it should be easy to spot.
On the other hand, Label is transparent for convenience/design:
Assuming it had a default color (in this case, blue) it wouldn't be very convenient, right ?
We get to the point, labels should melt into background, while fields such as textboxes should be easily distinguishable by nature.
What I find confusing is that the TextBox class has a BackgroundProperty which inherits from Control - no different than the Label. However, as can be seen the TextBoxes do not have their background colour changed.
Each control has a default style and control template. They define the required parts of a control, its apprearance and its visual states. The Background is one of the properties that may or may not be defined, depending on the control. For the controls that you use, the backgrounds are defined like this:
Label: Transparent
TextBox: SystemColors.WindowBrushKey
Grid: None, default value.
StackPanel: None, default value.
Consequently, the Label appears to be red, but is not. It is the StackPanel or Grid background that you see through its Transparent background. For the TextBox, the background does not change because of dependency property setting precedence. The background value of the default implicit style of TextBox just has a higher precedence than your local style setter.
What to do now? Assign the background with a higher precedence, e.g.:
Add it as a local value.
Define a style and assign it directly to the Style property of TextBox.
Define an implicit style for TextBox
<Style x:Key="{x:Type TextBox}" BasedOn="{StaticResource {x:Type TextBox}}">
<Setter Property="Control.Background" Value="Red"/>
</Style>
Although the Grid does not have a Control.Background property, but has a Panel.Background property, yet it still has its background property set, even though the property being set is Control.Background.
That is another implementation detail in WPF. As you can see in the reference source for Panel, it defines the Background property, but when you look at the reference source for Control, you can see that it does not define a Background property itself, but adds itself as the owner of the property defined by Panel. From the documentation of AddOwner:
Typically, AddOwner is used to add dependency properties to classes that do not already expose that dependency property through managed class inheritance (class inheritance would cause the wrapper properties to be inherited by the derived class, and thus would provide general members-table access to the dependency property already). AddOwner enables the property system to recognize a dependency property on a type that did not register that dependency property initially.
In other words, the properties work as if they were inherited and the XAML processor is smart enough to recognize that the Control.Background and Panel.Background properties are essentially the same.
Used BaseOn property For inherited
<Grid.Resources>
<Style x:Key="DefaultStyle" TargetType="{x:Type FrameworkElement}">
<Setter Property="Control.Background" Value="Red"/>
</Style>
<Style TargetType="TextBox" BasedOn="{StaticResource DefaultStyle}"/>
<Style TargetType="Label" BasedOn="{StaticResource DefaultStyle}"/>
</Grid.Resources>
<StackPanel>
<Label>First Content</Label>
<TextBox>First Edit</TextBox>
<StackPanel>
<Label>Second Content</Label>
<TextBox>Second Edit</TextBox>
</StackPanel>
</StackPanel>
</Grid>

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.

Update custom control Width and Height property using AdaptiveTriggers

I try to find how I can update the Width and Height properties of my custom control.
I have used the RadialProgressBar implementation found in UWP Community Toolkit pull request #828 for 1.5 milestone.
I can set the Width and Height on the custom control and it will work but I need to set different Width and Height values depending on the display resolution.
RadialProgressBar.xaml
RadialProgressBar.cs
Page.xaml
I'm doing something wrong I think but I can't find what.
Any idea?
I found the solution in the answer to this question UWP XAML Change Style of a target with VisualStateManager.
I need to put the VisualStateManager in the Template of the control.
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:RadialProgressBar">
<Grid x:Name="Grid">
<!-- Put the VisualStateManager definition here -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
I have updated RadialProgressBar.xaml file in my gist.

Use ScrollBar in DropDownButton?

I want use a scrollbar like ComboBox in my DropDown button, the structure actually is this:
<Controls:DropDownButton Content="Nazioni" Width="120" Margin="0, 0, 20, 0"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ScrollViewer.CanContentScroll="True"
ItemsSource="{Binding Countries}"
ItemTemplate="{StaticResource CombinedTemplate}"/>
but I don't see any ScrollViewer as you can see in the image below:
The drop-down of the DropDownButton already contains a ScrollViewer (it is named "SubMenuScrollViewer"), so scrolling through its items is supported out-of-the-box. The thing is, that particular ScrollViewer is styled differently than a default ScrollViewer - assuming we're talking about vertical scrolling, it has two buttons above and below the list, responsible for scrolling up and down respectively, as shown below:
So your best bet is to make that particular ScrollViewer use the default style rather than a custom one. By inspecting the MahApps.Metro source code we can see that the ScrollViewer in question is wired to use a dynamic resource with a key value of {ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}. So what you need to do is to supply a default style with that key for that control:
<Controls:DropDownButton (...)>
<Controls:DropDownButton.Resources>
<Style x:Key="{ComponentResourceKey ResourceId=MenuScrollViewer, TypeInTargetAssembly={x:Type FrameworkElement}}"
TargetType="{x:Type ScrollViewer}"
BasedOn="{StaticResource {x:Type ScrollViewer}}" />
</Controls.DropDownButton.Resources>
</Controls.DropDownButton>
This way the ScrollViewer within the drop-down will be styled with a default style shipped with MahApps.Metro.
To be certain that scrolling will behave as expected, you cannot rely on WPF to place a ScrollViewer where it should be.
As any content can be place on the dropdown, your best option is to drop a ScrollViewer straight onto the component.
This way, you can explicitly name it, and have access to its properties.
If you bind your list of countries to the lstContent box, you do away with all the messing.
<extToolkit:DropDownButton Content="Click Me" Margin="15" >
<extToolkit:DropDownButton.DropDownContent>
<ScrollViewer>
<ListBox Name="lstContent" ItemsSource="{Binding Countries}" ItemTemplate="{StaticResource CombinedTemplate}"/>
</ScrollViewer>
</extToolkit:DropDownButton.DropDownContent>
</extToolkit:DropDownButton>

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