WPF access to control in template. Issue with CalendarDayButton - c#

I'm trying to access at a Control in a Template. For this, I redefined control CalendarDayButton:
<Window.Resources>
<Style x:Key="myStyleDayButtonCalendar" TargetType="{x:Type CalendarDayButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CalendarDayButton}">
<Grid Name="gridCalendar">
<ContentControl Margin="5,1,5,1" Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Calendar CalendarDayButtonStyle="{StaticResource myStyleDayButtonCalendar}" Name="myCalendar" SelectedDatesChanged="Calendar_SelectedDatesChanged_1" />
</Grid>
It's OK for that. But when I want to access at my control GRID. Impossible:
private void Calendar_SelectedDatesChanged_1(object sender, SelectionChangedEventArgs e)
{
Grid gridInTemplate = (Grid)myCalendar.Template.FindName("gridCalendar", myCalendar) as Grid;
}
My grid is null. So, I tried with an other Control. With a Button:
<Window.Resources>
<Style x:Key="myStyleButton" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Name="myButton">
<Ellipse Fill="DarkBlue"></Ellipse>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Calendar CalendarDayButtonStyle="{StaticResource myStyleDayButtonCalendar}" Name="myCalendar" SelectedDatesChanged="Calendar_SelectedDatesChanged_1" />
<Button Style="{StaticResource myStyleButton}" Name="myButton2" Margin="92,99,518,338" Click="myButton2_Click_1"></Button>
</Grid>
Code behind:
private void myButton2_Click_1(object sender, RoutedEventArgs e)
{
Grid gridInTemplate = (Grid)myButton2.Template.FindName("myButton", myButton2);
}
And here gridInTemplate is NOT NULL. Why in the case dayCalendarButton gridInTemplate is NULL? I would like to avoid to use a VisualTreeHelper.

In your case, you're trying to find a CalendarDayButton in Calendar. But these are two different controls. For example, we have a style for the calendar:
<Style TargetType="{x:Type Calendar}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="BorderBrush" Value="Gray" />
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Calendar}">
<StackPanel x:Name="PART_Root" HorizontalAlignment="Center">
<CalendarItem x:Name="PART_CalendarItem" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In the code we can access to StackPanel like that:
private void Calendar_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
StackPanel StackPanelInTemplate = (StackPanel)MyCalendar.Template.FindName("PART_Root", MyCalendar) as StackPanel;
MessageBox.Show(StackPanelInTemplate.Name);
}
But it is in the calendar style not exists CalendarDayButton. This a separate control. At the CalendarDayButton we can only get style:
Style MyCalendarDayButton = (Style)MyCalendar.CalendarDayButtonStyle;
But not a template. The construction of the form:
CalendarDayButton MyCalendarDayButton = FindChild<CalendarDayButton>(MyCalendar, "gridCalendar");
It will not work. Does not work for the reason that it is not in the same visual tree as the calendar.
Conclusion: maybe just access to CalendarDayButton will not work because it is not see in the visual tree of calendar (have designed so developers). Although I may be mistaken. I have a similar problem encountered when working with DatePicker. There can not be simply so to get access to some parts of DatePicker, for example: get access to Watermark - http://matthamilton.net/datepicker-watermark.
The decision depends on why you need it this button. Try to move your functionality in triggers like Control, Style or create your own control, inherited from the CalendarDayButton class (that tiresome). Our use converters, example:
<Grid x:Name="CalendarDayButtonGrid">
<Grid.ToolTip>
<MultiBinding Converter="{StaticResource HighlightDate}">
<MultiBinding.Bindings>
<Binding />
<Binding RelativeSource="{RelativeSource FindAncestor,
AncestorType={x:Type Calendar}}" />
</MultiBinding.Bindings>
</MultiBinding>
</Grid.ToolTip>
<!-- End addition -->
</Grid>
P.S. Here you are told how to use WPF ToolKit access to CalendarDayButton: http://codesticks.wordpress.com/tag/wpf-toolkit/

Related

wpf change ComboBox border width programatically

How can I change the border width of a combobox programatically.
I tried :
combobox.BorderWidth = ...
but It did not work
Ilan
I found out from your answers the following solution :
<Window.Resources>
<Style x:Key="ComboBoxStyleKey" x:Name="ComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<Border x:Name="ContentPresenterBorder"
BorderBrush="{TemplateBinding Property=BorderBrush}"
BorderThickness="{TemplateBinding Property=BorderThickness}"
Background="{TemplateBinding Property=Background}"
CornerRadius="3">
<Grid>
<ToggleButton x:Name="DropDownToggle" />
<ContentPresenter x:Name="ContentPresenter" />
</Grid>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
Using this style I managed to change the border thickness
But now I have a new problem
The ToggleButton and the ContentPresenter does not work.
I want them to have the default behavior.
Is there a way to assign the default behavior to them (Something like style="default style")?
Thanks,
You can do it like this,
this.comboBox.BorderThickness = new Thickness(1, 1, 1, 3);

Removing full screen in xaml dynamically

This will probably be very simple for most of you, I am new to XAML and WPF.
I have an app that startes att full screen, I did this by adding
WindowState="Maximized"
WindowStyle="None"
I want to have a button that simply eliminates this part. I have a "Full screen" button in the xaml and by click he is calling a "FullScreen_Click" function in my code.
I just need to know what to write in the code that will eliminate the full screen if it is on full screen mode and restore it to full screen when it is not.
Try this:
private void FullScreen_Click(object sender, RoutedEventArgs e)
{
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
}
This will toggle between WindowState.Maximized and WindowState.Normal each time the Button is clicked.
For a xaml only technique just for reference to see a xaml example in comparison (but I would do #mm8's route, it's simpler);
1. Bind your property to that of another like:
<Window WindowState="{Binding Tag, ElementName=toggleState}" .... />
2. Use a `ToggleButton` or similar control and `Triggers`
.
<!-- like this PoC -->
<Grid>
<Grid.Resources>
<Style x:Key="cwWindowState_PoC" TargetType="{x:Type ToggleButton}">
<Setter Property="Tag" Value="Maximized"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<Border Background="{TemplateBinding Background}"/>
<ContentPresenter x:Name="MyContentPresenter"
Content="{TemplateBinding Tag}"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Tag" Value="Normal" />
</Trigger>
<Trigger Property="IsChecked" Value="False">
<Setter Property="Tag" Value="Maximized" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<ToggleButton x:Name="toggleState" Content="Click Me"
Background="Green"
Style="{StaticResource cwWindowState_PoC}"/>
</Grid>
Could also use DataTrigger but that requires interaction triggers instead of just a property setter from a template.

XamlParseException on extended ListView in Windows Phone 8.1

I've been scouring the internet for a week looking for an answer to my question, but nobody else seems to be hitting this issue.
I am migrating an app from Windows Phone 8 Silverlight to Windows Phone 8.1 WinRT, and am having an issue with a custom control I made. In the Silverlight app, I created a custom LongListSelector that I predefined a ItemTemplate for with custom binding and logic. I reuse this LongListSelector in a few different places in the app.
I am trying to do the same thing in my WinRT app, but with ListView. The issue is that when I try to include my custom extended ListView in any XAML page, I get an E_UNKNOWN_ERROR XamlParseException with the Line and Position number set to the end of the opening tag of my ListView.
Here is what my custom ListView's XAML looks like:
<ListView
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyAppNamespace"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:sys="using;System;assembly=mscorlib"
x:Class="MyAppNamespace.CustomListView"
x:Name="This"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400"
ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
.... my data template is here ...
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
And my code-behind is this:
namespace MyAppNamespace
{
public partial class CustomListView: ListView
{
public CustomListView()
{
this.InitializeComponent();
}
... event handlers and custom logic here ...
}
}
And here's how I reference it in another XAML page
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyAppNamespace"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="MyAppNamespace.SamplePage"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<local:CustomListView DataContext="{Binding Items}"/>
</Grid>
</Page>
The error shows up in Blend and in the design view of the xaml file in Visual Studio. When I run the app and navigate to the page where I am using this control, the error appears on the LoadComponent function call within the generated InitializeComponent.
The weird thing is that if I switch the root element to UserControl and put the ListView inside it (and update the base class in the code-behind), then everything works fine, but I'd rather just directly extend the ListView then wrap it in a UserControl.
Finally figured it out!!
I needed to override the PrepareContainerForItemOverride and then set the Loaded event for the ListViewItem passed in as the first parameter. Then, in the event handler, apply the callbacks as needed by traversing the element tree.
PrepareContainerForItemOverride will be called every time a row of the ExtendedListView is populated with a different element from the ItemsSource, but the Loaded callback will only be called once per row unless the row is unloaded (you can also add a callback handler for this if needed.
Below is some sample code to help out anyone else who has this issue!!
Here's the relevant contents of ExtendedListView.cs:
public sealed class ExtendedListView : ListView
{
public ExtendedListView()
{
this.DefaultStyleKey = typeof(ExtendedListView);
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
ListViewItem lvi = element as ListViewItem;
lvi.Loaded += lvi_Loaded;
}
void lvi_Loaded(object sender, RoutedEventArgs e)
{
ListViewItem lvi = sender as ListViewItem;
ApplyCallbacksToElement(lvi.ContentTemplateRoot);
}
private void ApplyCallbacksToElement(DependencyObject element)
{
if (null != element)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childrenCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(element, i);
// Code for adding element callbacks goes here
//
// For example:
// if (IsButtonAndMatchesCondition(child))
// {
// (child as Button).Click += button_Click;
// }
//
ApplyCallbacksToElement(child);
}
}
}
}
And here's the relevant content of the Generic.xaml file generated in the Themes folder created after making a new TemplatedControl:
<Style TargetType="local:ExtendedListView">
<Setter Property="IsTabStop"
Value="False" />
<Setter Property="TabNavigation"
Value="Once" />
<Setter Property="IsSwipeEnabled"
Value="True" />
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility"
Value="Disabled" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility"
Value="Auto" />
<Setter Property="ScrollViewer.HorizontalScrollMode"
Value="Disabled" />
<Setter Property="ScrollViewer.IsHorizontalRailEnabled"
Value="False" />
<Setter Property="ScrollViewer.VerticalScrollMode"
Value="Enabled" />
<Setter Property="ScrollViewer.IsVerticalRailEnabled"
Value="False" />
<Setter Property="ScrollViewer.ZoomMode"
Value="Disabled" />
<Setter Property="ScrollViewer.IsDeferredScrollingEnabled"
Value="False" />
<Setter Property="ScrollViewer.BringIntoViewOnFocusChange"
Value="True" />
<Setter Property="ItemContainerTransitions">
<Setter.Value>
<TransitionCollection>
<AddDeleteThemeTransition />
<ContentThemeTransition />
<ReorderThemeTransition />
<EntranceThemeTransition IsStaggeringEnabled="False" />
</TransitionCollection>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<ItemsStackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<!-- Put your custom DataTemplate here -->
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ExtendedListView">
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer x:Name="ScrollViewer"
TabNavigation="{TemplateBinding TabNavigation}"
HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
IsHorizontalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsHorizontalScrollChainingEnabled}"
VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
IsVerticalScrollChainingEnabled="{TemplateBinding ScrollViewer.IsVerticalScrollChainingEnabled}"
IsHorizontalRailEnabled="{TemplateBinding ScrollViewer.IsHorizontalRailEnabled}"
IsVerticalRailEnabled="{TemplateBinding ScrollViewer.IsVerticalRailEnabled}"
ZoomMode="{TemplateBinding ScrollViewer.ZoomMode}"
IsDeferredScrollingEnabled="{TemplateBinding ScrollViewer.IsDeferredScrollingEnabled}"
BringIntoViewOnFocusChange="{TemplateBinding ScrollViewer.BringIntoViewOnFocusChange}"
AutomationProperties.AccessibilityView="Raw">
<ItemsPresenter Header="{TemplateBinding Header}"
HeaderTemplate="{TemplateBinding HeaderTemplate}"
HeaderTransitions="{TemplateBinding HeaderTransitions}"
Footer="{TemplateBinding Footer}"
FooterTemplate="{TemplateBinding FooterTemplate}"
FooterTransitions="{TemplateBinding FooterTransitions}"
Padding="{TemplateBinding Padding}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You can only extend controls that inherit from UserControl the way you are trying (Like Page for example).
What you have to do is just create a class that inherits from ListView and modify default style for it.
<Style TargetType="my:CustomListView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="..">
...

WPF content control styling

I have a custom control which is basically a contentcontrol
public class PromoAlarmBox : ContentControl
{
static PromoAlarmBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PromoAlarmBox), new FrameworkPropertyMetadata(typeof(PromoAlarmBox)));
}
}
I add it to the containing user control
<controls:PromoAlarmBox Grid.Row="9" Grid.Column="1" />
If I add the style to the containing usercontrols resources everything works fine
<UserControl.Resources>
<Style TargetType="{x:Type controls:PromoAlarmBox}">
<Setter Property="ContentControl.ContentTemplate">
<Setter.Value>
<DataTemplate >
<Rectangle Fill="Blue" Stroke="Black" Height="20" Width="20"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
But if I add it to generic.xaml in the custom controls project , nothing is show
<Style TargetType="{x:Type local:PromoAlarmBox}">
<Setter Property="ContentControl.ContentTemplate">
<Setter.Value>
<DataTemplate >
<Rectangle Fill="Blue" Stroke="Black" Height="20" Width="20"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
I know the style is applied as I have other controls in same project whos styles are defined in generic.xaml, Anyone have any ideas?
A simple static should do the trick...
static PromoAlarmBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(PromoAlarmBox), new FrameworkPropertyMetadata(typeof(PromoAlarmBox)));
}
Although im not sure why there is a difference when you use a style as a local resource and when you use generic , this works for me
<Style TargetType="{x:Type local:PromoAlarmBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ContentControl">
<Rectangle VerticalAlignment="Stretch" Fill="Yellow" Stroke="Black" Height="20" Width="20"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

WPF: how to style a class like in css?

Let's say I have a UserControl with 4 Borders:
<Border />
<Border />
<Border />
<Border />
Now in my Resources I can go:
<Style TargetType="{x:Type Border}">
... change some properties here
</Style>
Now this is all good, but it will target all borders in my UserControl.
But what if I just want to target a subset of them?
I'd like to go:
<Border Class="Type1" />
<Border Class="Type1" />
<Border />
<Border />
And then go:
<Style TargetType="{x:Type Border}" TargetClass="Type1">
... change some properties here
</Style>
But this obviously doesn't exist, is there some other way I can achieve what I'm after?
Thanks
Though the syntax isn't quite as clean as in CSS, it is a lot more specific.
To build on your example, what you're looking for is:
<Border Style="{StaticResource Type1}" />
<Border Style="{StaticResource Type1}" />
<Border />
<Border />
And then go:
<Style TargetType="{x:Type Border}" x:Key="Type1">
... change some properties here
</Style>
Remember that WPF styles don't actually cascade like CSS does.
A more detailed styling reference:
https://web.archive.org/web/20141210000517/http://dotnetslackers.com/articles/wpf/StylesResourcesAndControlTemplatesInWPF.aspx
Something that I find most people are not aware of is WPF's ability to nest Styles within Style.Resources. For example:
<!-- Define a new style for Borders called InfoBox, that will have a red background,
and further override all buttons within it to have Yellow Text. An extra style,
"Strawberry" is also defined, that lets specific buttons be selected to be styled
as Green FG on DarkRed BG -->
<Style TargetType="{x:Type Border}" x:Key="InfoBox">
<Setter Property="Background" Value="Red"/>
<Style.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="DarkYellow"/>
</Style>
<Style TargetType="{x:Type Button}" x:Key="Strawberry">
<Setter Property="Foreground" Value="Green"/>
<Setter Property="Background" Value="DarkRed"/>
</Style>
</Style.Resources>
</Style>
...
<Border Style="{DynamicResource InfoBox}">
<StackPanel>
<Button Content="I am a banana!"/>
<Button Style="{DynamicResource Strawberry}" Content="I am red!"/>
</StackPanel>
</Border>
While not exactly the same as CSS (There isn't much support for standard pseudo-selectors), this gives you a huge amount of power and flexibility. Couple this with skillful use of ItemsControls and you can do some great things.
you can set the style directly on the <Border> using an x:key and the StaticResource (or DynamicResource) property of the Border. if you would like to change the style at runtime, then you should lean towards using the DynamicResource over the StaticResource.
<Style x:Key="something" TargetType="{x:Type Border}">
</Style>
<Border style="{StaticResource something}"/>
<Style x:Key="styleKey" TargetType="{x:Type Border}">
... change some properties here
</Style>
and
<Border Style="{StaticResource styleKey}"

Categories

Resources