How to change XAML elemen't template from code-behind? - c#

I have next button's style defined in resources:
<Style x:Key="OKBtn" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Rectangle .../>
<TextBlock x:Name="Text" ..>
<Run Language="en-en" Text="OK"/>
</TextBlock>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And I want in some specified case from code change Button's text.
I.e. change "OK" (<Run Language="en-en" Text="OK"/>) to "Accept".
How can I do that?
Is it possible to access this TextBlock "Text" and change content exactly for my one button, but not for all OK buttons?
My button:
<Button x:Name="OkButton" Style="{DynamicResource OKBtn}" />

You can borrow some props from template Template, for example Tag property. So the TextBlock text in the ControlTemplate should be like this.
<Run Language="en-en" Text="{Binding RelativeSource={RelativeSource Mode=TemplatedParent}, Path=Tag}"/>
And you can change the button caption by setting it's Tag property.
OkButton.Tag = "Accept";
And for not set all button texts manually you can create some ValueConverter to set TextBlock text in the ControlTemplate to the "Ok" whenever Tag property is empty.

At first, you should declare ContentPresenter to show any object in your Content property of Button control.
<Style x:Key="OkBtn" TargetType="Button">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Rectangle/>
<ContentPresenter Content="{Binding Path=Content, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"></ContentPresenter>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Then, it is possible to set another Content by using code behind or binding:
By code behind:
okButton.Content="desirableText";
By binding:
<Button x:Name="OkButton" Style="{DynamicResource OKBtn}" Content="{Binding FooText}" />
private string fooText;
public string FooText
{
get { return fooText; }
set
{
fooText = value;
OnPropertyChanged("FooText");
}
}

Related

I want add picture in GridView tooltip before open

I create in XAML RadGridVieaw controls custom tooltip and when tool tip before opening, I want read row and take ID and then load picture from database.
1 step I create custom tool tip
<Style TargetType="{x:Type telerik:GridViewRow}" BasedOn="{StaticResource GridViewRowStyle}" >
<Setter Property="ToolTipService.IsEnabled" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type telerik:RadGridView}}}" />
<Setter Property="ToolTip" >
<Setter.Value>
<ToolTip ToolTipService.ShowDuration="1000000" >
<Grid >
<Border HorizontalAlignment="Left" Height="170" Margin="5,0,0,0"
VerticalAlignment="Top" Width="130" BorderBrush="#FFCED8DA" BorderThickness="1" Padding="1">
<Image x:Name="GeneralTabItem_EmployeeImage" Source="{Binding CRAPhotoPhoto, Converter={StaticResource BinaryArrayToURIConverter}}" Stretch="UniformToFill"
Width="120" Height="160"/>
</Border>
<Grid/>
<telerik:RadGridView x:Name="ExtnedPrisonerInfoView_DataInput_ListGrid" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch" ToolTipService.ShowDuration="100000000"
DataContext="{Binding ''}" ToolTipOpening="OnContentChanged">
</telerik:RadGridView>
2 step I want to code behind catch event opening Tool tip.
3 step attach picture to the opening tool tip Image in Name="GeneralTabItem_EmployeeImage"
Please help me ToolTipOpening event is not working.
This should work as far as the event handler is concerned:
<Style TargetType="{x:Type telerik:GridViewRow}" BasedOn="{StaticResource GridViewRowStyle}" >
<EventSetter Event="ToolTipOpening" Handler="outerGrid_ToolTipOpening" />
...
But if you want to do something with the Image in the ToolTip, you need to wait until it has been created. You might as well handle the Loaded event of the Image and set the Source of it in there:
private void GeneralTabItem_EmployeeImage_Loaded(object sender, RoutedEventArgs e)
{
Image img = sender as Image;
//set source...
}
XAML:
<Setter Property="ToolTip" >
<Setter.Value>
<ToolTip ToolTipService.ShowDuration="1000000">
<Grid>
<Border HorizontalAlignment="Left" Height="170" Margin="5,0,0,0"
VerticalAlignment="Top" Width="130" BorderBrush="#FFCED8DA" BorderThickness="1" Padding="1">
<Image x:Name="GeneralTabItem_EmployeeImage" Stretch="UniformToFill"
Width="120" Height="160"
Loaded="GeneralTabItem_EmployeeImage_Loaded"/>
</Border>
<Grid/>
</Grid>
</ToolTip>
</Setter.Value>
</Setter>
Obviously you won't be able to set the Source property of an Image that resides in a ToolTip before the ToolTip has been opened because by then there is no Image. So this doesn't make much sense.
Create a simple tooltip first:
<Style TargetType="telerik:GridViewRow" BasedOn="{StaticResource
GridViewRowStyle}">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<TextBlock Text="{Binding EmployeeName}"></TextBlock>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
Verify that works and if it does start adding more complex things like converters and ToolTipService stuff one at a time to see what is it exactly that is invalidating your XAML because XAML error is the only reason why the event would not be firing...

Closable tab with tab item being a user control

I am working on a project in C# WPF. I have a tab container and I want to dynamically load different types of tabs into the tab container as the user requires. As an example I am doing something like the following:
tabContainer.Items.Add(new MyUserControl());
I want each tab to have a close button so the tab can be removed the container when the user no longer requires it.
I found this code project example but from what I can see you are a loading a user control which contains the xaml for the tab itself, not the tab content or am I missing something.
How can I load in my User Control into the tab container, but also have the tab closable.
Currently the tab that I am loading in uses some static binding to set the tab title using the following:
<TabControl x:Name="tabContainer" Grid.Column="2" Margin="10,45,0,0" RenderTransformOrigin="0.5,0.55" Grid.ColumnSpan="3">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding TabHeader}" />
</Style>
</TabControl.Resources>
</TabControl>
My user control then has a `public string TabHeader{get;set;} which gets set in the constructor depending on what constructor of my user control is used.
You will have to define the close Button yourself. You could for example do this in the HeaderTemplate of the TabItem:
<TabControl x:Name="tabContainer">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding TabHeader}" />
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<Button Content="x" Click="Button_Click_2"
Tag="{Binding DataContext, RelativeSource={RelativeSource AncestorType=TabItem}}"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
</TabControl>
The Tag property is bound to the UserControl in the Items collection which you can remove in the click event handler of the Button, like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
tabContainer.Items.Add(new MyUserControl());
}
private void Button_Click_2(object sender, RoutedEventArgs e)
{
Button button = sender as Button;
tabContainer.Items.Remove(button.Tag);
}
}
If you want to add a close button to each tab, that would be in the TabItem style ControlTemplate. Normally you'd specify the data context (i.e. the data only that's driving the content) in Content and then specify the look in ContentTemplate. If your Content is a UserControl then you don't specify the ContentTemplate since a UserControl knows how to draw itself.
For my sins, I've added close-tab buttons to the WPF TabControl. I ended up putting the close button in the ItemTemplate. Here's a minimal version that works with the way you're populating the TabControl and the header content:
<TabControl
>
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Header" Value="{Binding TabHeader}" />
</Style>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Label
Content="{Binding}"
Grid.Column="0"
/>
<Button
VerticalAlignment="Center"
Grid.Column="1">
<Path
Data="M 0, 0 L 12, 12 M 12,0 L 0,12"
Stroke="Red"
StrokeThickness="2"
Width="12"
Height="12"
/>
</Button>
</Grid>
</DataTemplate>
</TabControl.ItemTemplate>
<local:UserControl1 TabHeader="First Item" />
<local:UserControl1 TabHeader="Second Item" />
</TabControl>

Window Style Resource Does Not Override UserControl Property When Present

Given I have a control from somewhere called SomeControl
In MyUserControl.xaml I use SomeControl like so:
<Grid.Resources>
<Window.Resources>
<Style TargetType="local:SomeControl">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<TextBlock Text="FOO"/>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
</Grid.Resources>
<Grid>
<!-- Others controls in here -->
<local:SomeControl />
</Grid>
In Window.xaml:
<Window.Resources>
<Style TargetType="local:SomeControl">
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip>
<TextBlock Text="BAR"/>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<local:MyUserControl />
</Grid>
The result is it will display Foo when I want it to display Bar.
If I delete the ToolTip from the UserControl, the Window's style is used and it displays Bar like I expect.
Why is the Window style not overriding the UserControl explicit ToolTip property when present, but does when removed?
For the record, I've also tried changing MyUserControl to use a DynamicResource of the same x:Key names and had no affect.
Changing both to ToolTipService.ToolTip also had the same results.
EDIT: I fixed the example to demonstrate that even if the ToolTip is not set on a local level, it still doesn't override the style.
Unless someone can answer how to do override the children's style for the tooltip, I ended up making ToolTip object a dependency property on MyUserControl and passing it down to SomeControl.
The dependency property I made is called ExampleToolTip
MyUserControl.xaml:
<UserControl x:Name='MainControl'>
<UserControl.Resources>
<ToolTip x:Key="DefaultSomeControlToolTip">
<TextBlock Text="FOO"/>
<ToolTip>
</UserControl.Resources>
<Grid>
<local:SomeControl ToolTip="{Binding ExampleToolTip, ElementName=MainControl, TargetNullValue={StaticResource DefaultSomeControlToolTip}}"/>
</Grid>
</UserControl>
Window.xaml:
<Grid>
<local:MyUserControl>
<local:MyUserControl.ExampleToolTip>
<ToolTip>
<TextBlock Text="BAR"/>
</ToolTip>
</local:MyUserControl.ExampleToolTip>
</local:MyUserControl>
</Grid>
Now I can use MyUserControl with it's special "FOO" tooltip on SomeControl and Window has its "BAR" tooltip overriding it.

DataTemplate Binding to multiple object properties

I'm using button styling, with data template, and having problem with showing text (Name and Number properties of my Table objects).
This is xaml:
<Style x:Key="btnTable" TargetType="{x:Type Button}" >
<Setter Property="Background" Value="Transparent" />
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type my:Tables}">
<Grid>
<Image VerticalAlignment="Center" Source="/Images/Ico/table-40x40.png" />
<TextBlock Text="{Binding Path=Naziv}" />
<Ellipse Canvas.Top="30" Canvas.Left="30" Fill="#FF6A4E8C" />
<TextBlock Text="{Binding Number}" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Then, in .cs I create buttons like this:
Button b = new Button();
Style myStyle = (Style)Resources["btnTable"];
b.Style = myStyle;
b.DataContext = myTableItem;
If I test buttons click event, it has in DataContext property valid Table object.
I have tried replacing Binding Path width Binding Path=DataContext.Name, adding RelativeSource=Self, but nothing helps.
Additionally, I tried creating buttons using ItemsControl, but have the same issue.
b.Content = myTableItem;
It's the ContentTemplate. It's a template for the Content.

Command Binding in ControlTemplate with Attached Property

I'm trying to set up a system that would allow me to attach a string value to a ComboBoxItem and display it next to the ComboBoxItem's content, without having to explicitly nest a StackPanel inside every individual ComboBoxItem or use a custom control.
So what I've done is created a DependencyProperty called "Header" and attach it to the ComboBoxItem, and I've overridden the ComboBoxItem template to contain the stack panel with a TextBlock that binds its Text to the attached Header property of the ComboBoxItem.
The problem I am having is that the only text that ever appears in the TextBlock at runtime is whatever I set the default value of the dependency property to in metadata. Any changes to the attached property on the ComboBoxItems thereafter is not reflected in the TextBlock.
Here is my DependencyProperty definition:
public class AttHeader : DependencyObject
{
public static readonly DependencyProperty HeaderProperty = DependencyProperty.RegisterAttached("Header", typeof(string), typeof(AttHeader));
public static void SetHeader(DependencyObject d, string value)
{
d.SetValue(HeaderProperty, value);
}
public static string GetHeader(DependencyObject d)
{
return (string)d.GetValue(HeaderProperty);
}
}
Here is my style and template:
<Style TargetType="ComboBoxItem">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<StackPanel Orientation="Horizontal">
<ContentPresenter />
<TextBlock Name="HeaderHost" Text="{Binding Path=(local:AttHeader.Header), RelativeSource={RelativeSource Self}}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And here is where I create a few ComboBoxItems:
<ComboBox SelectedIndex="0">
<ComboBoxItem local:AttHeader.Header="Isometric">
<Image Source="../images/viewTypeIso.png" Stretch="None"/>
</ComboBoxItem>
<ComboBoxItem local:AttHeader.Header="Top">
<Image Source="../images/ViewTypeTop.png" Stretch="None"/>
</ComboBoxItem>
</ComboBox>
Even setting the value of the attached properties of these ComboBoxItems when they are created has no effect on the TextBlocks within.
I'm assuming the binding is valid, because I can set a default value for the dependency property and each ComboBoxItem will always display that value next to its image.
I've set a breakpoint in SetHeader and it is indeed called when those ComboBoxItems are constructed.
Is there something I am doing wrong or is there some limitation to Binding that I'm unaware of?
your binding isn't correct , TextBlock is now a child of the ComboBoxItem
try binding to :
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<StackPanel Orientation="Horizontal">
<ContentPresenter />
<TextBlock Name="HeaderHost"
Text="{Binding Path=local:AttHeader.Header,
RelativeSource={RelativeSource Mode=FindAncestor
,AncestorType=ComboBoxItem}}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
I still haven't gotten the header binding to work with that implementation, but here's a super easy convenient workaround: Use HeaderedContentControls instead of ComboBoxItems, and instruct your ComboBox to orient the Header Horizontally by implementing the following Style:
<Style TargetType="HeaderedContentControl">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="HeaderedContentControl">
<StackPanel Orientation="Horizontal">
<ContentPresenter />
<ContentPresenter ContentSource="Header" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Categories