How to get control used in template in user control code? - c#

How could I get a DataGrid control used in xaml style template to use it in code?
<UserControl>
<UserControl.Resources>
<Style x:Key="MyComboBoxStyle" TargetType="{x:Type ComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid x:Name="MainGrid" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="0" MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" />
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup"
Grid.ColumnSpan="2">
<Microsoft_Windows_Themes:SystemDropShadowChrome x:Name="Shdw">
<Border x:Name="DropDownBorder">
<DataGrid x:Name="PART_PopupDataGrid" />
</Border>
</Microsoft_Windows_Themes:SystemDropShadowChrome>
</Popup>
<ToggleButton Grid.ColumnSpan="2"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
IsChecked="{Binding Path=IsDropDownOpen,
Mode=TwoWay,
RelativeSource={RelativeSource TemplatedParent}}"
Style="{StaticResource ComboBoxReadonlyToggleButton}" />
<ContentPresenter Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
IsHitTestVisible="false"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid>
<ComboBox Name="ComboGrid" Style="{DynamicResource DataGridComboBoxStyle}" />
</Grid>
</UserControl>
There is how I tried to get DataGrid control in user control, but not successfully:
var v1 = this.FindName("PART_PopupDataGrid");
var v2 = this.Template.FindName("PART_PopupDataGrid", this);
var v3 = ComboGrid.FindName("PART_PopupDataGrid");
How could I get this control in code?

This is a very common requirement. In fact, it's so common that Microsoft even have a page on MSDN specifically for this:
How to: Find ControlTemplate-Generated Elements
Basically, if you have a reference to the ComboBox that the ControlTemplate is applied to (let's call it ComboBox), then you should be able to do this:
DataGrid dataGrid =
ComboBox.Template.FindName("PART_PopupDataGrid", ComboBox) as DataGrid;

Related

How to remove arrow from ColorCanvas dropdown control in wpf C#

I am using c# wpf ColorCanvas control for using colors in my application.I want to remove dropdown arrow from ColorCanvas control.
I am using this Xaml Code for ColorCanvas
<xctk:ColorPicker x:Name="canvas_Copy" ColorMode="ColorCanvas" VerticalAlignment="Top" Margin="93,323,409,0" />
For clarity check this image.I want to remove this Arrow form ColorCanvas.
You should use a custom ButtonStyle:
<xctk:ColorPicker x:Name="canvas_Copy" ColorMode="ColorCanvas" VerticalAlignment="Top" Margin="93,323,409,0" >
<xctk:ColorPicker.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</xctk:ColorPicker.Resources>
<xctk:ColorPicker.ButtonStyle>
<Style TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton" xmlns:chrome="clr-namespace:Xceed.Wpf.Toolkit.Chromes;assembly=Xceed.Wpf.Toolkit">
<Grid SnapsToDevicePixels="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}"
SnapsToDevicePixels="True">
<ContentPresenter Content="{TemplateBinding Content}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" />
</Border>
<chrome:ButtonChrome x:Name="ToggleButtonChrome"
Grid.Column="1"
CornerRadius="0,2.75,2.75,0"
Visibility="{Binding ShowDropDownButton, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=xctk:ColorPicker}, Converter={StaticResource BooleanToVisibilityConverter}}"
RenderChecked="{Binding IsOpen, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=xctk:ColorPicker}}"
RenderEnabled="{Binding IsEnabled, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=xctk:ColorPicker}}"
RenderMouseOver="{TemplateBinding IsMouseOver}"
RenderPressed="{TemplateBinding IsPressed}">
</chrome:ButtonChrome>
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</xctk:ColorPicker.ButtonStyle>
</xctk:ColorPicker>
Use ColorCanvas control from the same namespace instead of ColorPicker.
finds its css file (or its styling file) and change its color to match its background

Click element within Data Grid Column Header

I have applied a custom style to my DataGrid's column headers. Here is a simplified version of it:
<Style x:Key="DataGridColumnHeaderStyle" TargetType="{x:Type DataGridColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DataGridColumnHeader">
<Grid>
<Border BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" BorderThickness="{TemplateBinding BorderThickness}" HorizontalAlignment="{TemplateBinding HorizontalAlignment}" VerticalAlignment="{TemplateBinding VerticalAlignment}">
<Grid>
<StackPanel Orientation="Horizontal">
<ContentPresenter Margin="5 0 5 0" HorizontalAlignment="Center" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<Image IsHitTestVisible="True" Source="pin.png">
<Image.InputBindings>
<MouseBinding MouseAction="LeftClick" Command="{x:Static myView:MyCommand}" CommandParameter="{Binding}" />
</Image.InputBindings>
</Image>
</StackPanel>
</Grid>
</Border>
<Thumb x:Name="PART_LeftHeaderGripper" Style="{StaticResource DataGridColumnHeaderResizeThumb}"/>
<Thumb x:Name="PART_RightHeaderGripper" HorizontalAlignment="Right" Style="{StaticResource DataGridColumnHeaderResizeThumb}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am struggling to get the command to fire when clicking the image. Is there something I am missing? Is there a different way that I can fire a command when clicking an element (i.e. an image) within the DataGridColumnHeader?
Some more details:
MyCommand is defined in the Window's CommandBindings. I have not included this code here. I have other commands defined similarly for other elements (i.e. DataGridCell) that work OK. It seems there is something specific to the way the DataGridColumnHeader element that prevents the command to be fired.
It seems like you're binding to the type MyCommand under xmlns myView; instead, you should be binding to an instance of MyCommand (assuming class MyCommand implements ICommand). You may consider implementing singleton here. Or you could define a static instance of MyCommand anywhere.

Accessing a control which is in a contentpresenter in c#

How to access a named control that is in the content template of the contentpresenter. how to access the webview control(x:name=detView) from cs file.
<ContentPresenter
x:Name="DetailContentPresenter"
Grid.Row="0"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
Content="{x:Bind coll.SelectedItem,Mode=OneWay}">
<ContentPresenter.ContentTemplate>
<DataTemplate x:DataType="data:coll_Details" x:Name="ttt">
<Grid>
<WebView DefaultBackgroundColor="#F5F5F5" x:Name="detView" Source="ms-appx-web:///Assets/Web/collDetails.html"/>
</Grid>
</DataTemplate>
</ContentPresenter.ContentTemplate>
<ContentPresenter.ContentTransitions>
<TransitionCollection/>
</ContentPresenter.ContentTransitions>
</ContentPresenter>
If you are using ContentPresenter as ControlTemplate like the example of Official Documentation.
You can get the template through the controlName.ContentTemplateRoot. I made a demo from the Example of official documentation above, and put a webview inside the DataTemplate.
MainPage.xaml:
<Page.Resources>
<Style TargetType="HyperlinkButton" x:Key="myStyle" >
...
<Setter Property="Template" x:Name="presenterSetter">
<Setter.Value>
<ControlTemplate TargetType="HyperlinkButton">
<Grid x:Name="rootGrid">
...
<Border x:Name="Border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Margin="3">
<ContentPresenter x:Name="MyContentPresenter"
Content="{TemplateBinding Content}"
ContentTransitions="{TemplateBinding ContentTransitions}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
>
<ContentPresenter.ContentTemplate>
<DataTemplate x:Name="ttt">
<Grid>
<WebView Source="ms-appx-web:///Assets/Web/default.html" Name="myWebView"/>
</Grid>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Border>
<!--focus visuals omitted-->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel VerticalAlignment="Bottom">
<HyperlinkButton Name="myHyperlink" Style="{StaticResource myStyle}">This is a link</HyperlinkButton>
<Button Click="Button_Click" Name="myBtn">Click Me</Button>
</StackPanel>
</Grid>
And I can get the WebView using the codes below:
private void Button_Click(object sender, RoutedEventArgs e)
{
var myView= ((Grid)myHyperlink.ContentTemplateRoot).Children[0] as WebView;
}
In C#, use this code to find any control which is present in your ContentPresenter.
If a TextBlock is present in your ContentPresenter then first create a object of the TextBlock then cast it and then find control.
TextBlock myTextBlock = (TextBlock)ttt.FindName(“textBlock”, DetailContentPresenter);

Can't access the Text property of a TextBlock in a ControlTemplate

I'm trying to data bind a property to a Label and change its color and text in response to the value of the bound property. I'm using a ControlTemplate to change the color and Text because changing the Content of a Label in response to DataTriggers didn't work (the text never appeared).
So, using a ControlTemplate works when defining it inline on the Label, but does not seem to work when defining the template as a resource.
The code below is a simpler example to demonstrate the problem.
This is what I have so far:
<ResourceDictionary>
<ControlTemplate x:Key="baseTemplate" TargetType="{x:Type Label}">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="24"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="InnerTextBlock" Grid.Column="1"
Text="{TemplateBinding Label.Content}" <!-- An attempt to tie the Text here to the Label's Content property -->
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
/>
</Grid>
</ControlTemplate>
<Style x:Key="availableLabelStyle" TargetType="{x:Type Label}">
<Setter Property="Background" Value="#FF567E4A"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Content" Value="Available"/>
<Setter Property="Template" Value="{StaticResource baseTemplate}"/>
</Style>
</ResourceDictionary>
<Label x:Name="StatusLabel"
Style="{StaticResource availableLabelStyle}"
Grid.Column="1"
HorizontalAlignment="Left"
Margin="111,71,0,0"
VerticalAlignment="Top" Width="124"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Height="18"
Padding="2"
/>
The problem is that the Content property in the Setter for the 'availableLabelStyle' does not seem to work. No text appears when this style is applied to a Label.
Is there a better way to do the same thing here AND get the text to appear in the Label?
Thanks in advance for any help on this.
The code you have is working. Here is my complete example:
<Window x:Class="WPFTestApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="baseTemplate" TargetType="{x:Type Label}">
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="24"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="1" Text="{TemplateBinding Label.Content}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"/>
</Grid>
</ControlTemplate>
<Style x:Key="availableLabelStyle" TargetType="{x:Type Label}">
<Setter Property="Background" Value="#FF567E4A"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Content" Value="Available"/>
<Setter Property="Template" Value="{StaticResource baseTemplate}"/>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Label x:Name="StatusLabel" Style="{StaticResource availableLabelStyle}"
Grid.Column="1"
HorizontalAlignment="Left"
Margin="111,71,0,0"
VerticalAlignment="Top" Width="124"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Height="18"
Padding="2"/>
</Grid>
</Window>
Which produces the following output:
Another way to do this is to use ContentPresenter instead of TextBlock. You can still add all of your additional properties to it (at least the ones you have shown), and it would allow displaying content other than text.

Can I reuse a Style using different DataTrigger Bindings?

I am working on a WPF 4.0 window with many status indicators using Labels. In one example, the Labels turn GREEN and read "Available" if the bound property's enumeration Foo is AVAILABLE. The labels turn RED and read "Not Available" if the bound property is NOTAVAILABLE. I also have other labels that indicate status of different bound enum Bar, and they turn different colors and have different content based on this value.
I am able to successfully bind and turn one label's color and text based on the value of a bound property. I use a rather lengthy ControlTemplate in a DataTrigger's Setter just to change the label's text.
Here's what I have so far:
<Window> ...
xmlns:cst="clr-namespace:CstCommonTypes;assembly=CstCommonTypes"
...
</Window>
<Label x:Name="Avail_Out_LBL" HorizontalAlignment="Left" Margin="111,44,0,0" VerticalAlignment="Top" Width="124" Height="18" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" SnapsToDevicePixels="False" Grid.Column="1" Padding="0">
<Label.Style>
<Style TargetType="{x:Type Label}">
<Setter Property="Background" Value="#FFC08100"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="24"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Degraded"
Grid.Column="1"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding MonitorAndControlData.Availability}" Value="{x:Static cst:Foo.Available}">
<Setter Property="Background" Value="#FF567E4A"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="24"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="24"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Available"
Grid.Column="1"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding MonitorAndControlData.Availability}" Value="{x:Static cst:Foo.NotAvailable}">
<Setter Property="Background" Value="LightCoral"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="22"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="22"/>
</Grid.ColumnDefinitions>
<TextBlock Text=" Not Available"
Grid.Column="1"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
The above works, but I was wondering if I can reuse either the Style and give it different DataTrigger bindings or just reuse the ControlTemplate to cut down on xaml code. I tried to see if I could define a resource, but I couldn't figure out how to give it different Template Bindings for all the Labels.
Any help would be greatly appreciated.
If you need more properties to bind to, then turn it into a custom control (i.e. a class that inherits from Control), then you can add additional dependency properties on said control, which you can then bind to in the template.
It won't be possible to reuse the style if the bindings are not the same.
What you could do is make yourself a user control for your status indicators labels, Inside which you will create a Dependency Property (lets name it Availability) on which you will bind your Triggers to. But, you will have to make sure that you will always need the same type for your triggers. If the Availability binding is not of the same type for each of your labels, this solution won't work.
Here is a link on how to use Dependency Properties :
http://www.wpftutorial.net/dependencyproperties.html

Categories