Borders around ControlTemplate for ComboBox - c#

I have the following ControlTemplate for the Validation of a ComboBox
<ControlTemplate x:Key="ComboBoxHighlightTemplate" TargetType="Control">
<Grid ClipToBounds="False">
<Border BorderBrush="{StaticResource fokusBrush}" BorderThickness="2.5" Margin="-1" Visibility="{Binding ElementName=adornedElementHighligh,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent, Converter={StaticResource inverseErrorContentVisibilityConverter}}">
<AdornedElementPlaceholder Name="adornedElementHighligh"/>
</Border>
<Border BorderBrush="Red" BorderThickness="1" Margin="-1" Visibility="{Binding ElementName=adornedElement,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent, Converter={StaticResource errorContentToErrorVisibilityConverter}}">
<AdornedElementPlaceholder Name="adornedElement" />
</Border>
<Image HorizontalAlignment="Right" VerticalAlignment="Top" Visibility="{Binding ElementName=adornedElement,
Path=AdornedElement.(Validation.Errors)[0].ErrorContent, Converter={StaticResource errorContentToErrorVisibilityConverter}}"
Width="16" Height="16" Margin="0,-9,-9,0" Source="{x:Static helper:ImageHelper.ErrorImage}"
ToolTip="{Binding ElementName=adornedElement, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" />
</Grid>
</ControlTemplate>
Depending on the ErrorContent I decide which Border I draw around the ComboBox.
The Converter ErrorContentToErrorVisibilityConverter looks like
internal class ErrorContentToErrorVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string)
{
if (((string) value).EndsWith("not an error"))
return Visibility.Hidden;
}
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Everything just works fine. But if I produce an error in my ComboBox so that the ControlTemplate for Errors is shows, the bounds of the drawn borders are not at the "real" borders of the ComboBox.
The usage of the ControlTemplate looks like:

here is a sample template, I did try to simplify it for you
<ControlTemplate x:Key="ComboBoxHighlightTemplate"
TargetType="Control">
<Grid ClipToBounds="False">
<AdornedElementPlaceholder Name="adornedElement" />
<Border BorderBrush="Red"
BorderThickness="1"
x:Name="errorBorder" />
<Image HorizontalAlignment="Right"
VerticalAlignment="Top"
x:Name="image"
Width="16"
Height="16"
Margin="0,-9,-9,0"
Source="{x:Static helper:ImageHelper.ErrorImage}"
ToolTip="{Binding ElementName=adornedElement, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" />
</Grid>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding AdornedElement.(Validation.Errors)[0].ErrorContent, ElementName=adornedElement,Converter={StaticResource errorContentToErrorVisibilityConverter}}"
Value="Hidden">
<Setter Property="BorderBrush"
TargetName="errorBorder"
Value="{StaticResource fokusBrush}" />
<Setter Property="BorderThickness"
TargetName="errorBorder"
Value="2.5" />
<Setter Property="Visibility"
TargetName="image"
Value="Collapsed" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
try it and let me know how close is this to your results, we may also simplify the trigger a bit further, I did this on assumptions.

Related

Style Trigger using tabindex on tabcontrol is not working when itemsource is referenced

I have tabcontrol which uses an observable collection of tabitems as itemsource. I dont want the close button for the first tab alone. So i added the style trigger
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid Background="White">
<Border Name="Border" BorderBrush="#1b9ed2" Margin="6,0,12,0" Background="White">
<ContentPresenter Height="30" x:Name="ContentSite" ContentSource="Header" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="5,15,5,-5">
</ContentPresenter>
</Border>
<Button Background="Transparent" BorderBrush="Transparent" Name="TabCloseButton" Click="TabCloseButton_Click" HorizontalAlignment="Right" VerticalAlignment="Bottom" ToolTip="Close" HorizontalContentAlignment="Right" Padding="0">
<materialDesign:PackIcon Kind="Close" Foreground="Gray" HorizontalAlignment="Right"/>
</Button>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TabIndex" Value="0">
<Setter TargetName="TabCloseButton" Property="Visibility" Value="Collapsed"/>
</Trigger>
......
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FontSize" Value="12"/>
</Style>
</TabControl.Resources>
It works if i just added the items in xaml
<TabControl Name ="ConnectionsTab">
<TabItem Header="...." TabIndex="0"></TabItem>
<TabItem Header="...." TabIndex="1"></TabItem>
</TabControl>
But it doesn't works when I make the itemsource to the tabcontrol
private ObservableCollection<TabItem> tabItems = new ObservableCollection<TabItem>();
inside constructor
tabItems.Add(new TabItem { Header = "Connections", Content = new ResourceAccountDisplayUserControl(response), TabIndex = 0 });
ConnectionsTab.ItemsSource = tabItems;
I don't know why it does not work. Any idea or code to make it work will be helpful.
First of all, TabIndex is not the index if your TabItem inside the TabControl. So, you need a different way to determine the actual tab position.
If your data source implements IList, you could for example write a MultiValueConverter that determines the position of an item within a collection:
public class MyTabItemIndexConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
// TODO: add some validation to avoid unsupported values
var c = (IList)values[1];
return c.IndexOf(values[0]);
}
public object[] ConvertBack(object values, Type[] targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Side Note: there are probably other ways to determine the index like Determining index of a tabitem.
Then you can use this converter, to store the actual item index somewhere (or use it directly). In this example, I set the TabItem.Tag property to the item index:
<Window.Resources>
<local:MyTabItemIndexConverter x:Key="tabItemIndexConverter"/>
</Window.Resources>
[...]
<TabControl Name="tabControl1" ItemsSource="{Binding}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Tag">
<Setter.Value>
<MultiBinding Converter="{StaticResource tabItemIndexConverter}">
<Binding />
<Binding ElementName="tabControl1" Path="ItemsSource" />
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</TabControl.ItemContainerStyle>
[...]
</TabControl>
Now with TabItem.Tag you have a property that you can actually use in order to implement your trigger logic.

Why TemplateBinding can't binding Button.Content?

I'm new in WPF and i'm executing some simulations to try understanding the binding "things" (Like {Binding}, {TemplateBinding}...)
Ok, so I make this sample here to test TemplateBinding and works well:
<Button Width="100" Height="100">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Image Source="image.png" Width="{TemplateBinding Width}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
Now I tried the same but with others properties:
<Button Width="100" Height="100" Content="image.png">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Image Source="{TemplateBinding Content}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
In the first case, the binding occurs perfectly. But in the second nothing happens.
What I'm missing here? There is some properties that can be bind others can't?
I'm really lost here, can someone explain to me why one works and the other not?
TemplateBinding is evaluated at compile time and does not provide automatic value type conversion (in this case string to ImageSource). Just use TemplatedParent relative source binding instead
<Image Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
Another thing, not directly related to this problem, is that if you want to use TemplateBinding for other properties of the same type make sure you set TargetType accordingly. TemplateBinding is evaluated against ControlTemplate type and default TargetType is System.Windows.Controls.Control
<ControlTemplate TargetType="{x:Type Button}">
Your bindings should have matching types. Button.Content is an object while Image.Source expects an Uri. Instead of Content use Tag and convert it via a converter:
<Button Width="100" Height="100" Tag="image.png">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Image Source="{TemplateBinding Tag, Converter={StaticResource ObjectToUriConverter}}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Button.Style>
</Button>
Converter
public class ObjectToUriConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null)
return null;
return new Uri(value.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
I think you might get the Content is not recognised or is not accssible error , rather than you can use <Image Source="{Binding Content,ElementName=btn}" /> where btn is the Button Name.

WPF Set Label Background according to its Value using DataBinding

I am currently making a grid that in each cell there is a label, the content in the labels are coming from a list using DataBinding.
I am trying to change the color of each cell depending on the value in the label. Eg if value = 1 then the background must be black.
This is the code i have now:
<Window.Resources>
<DataTemplate x:Key="DataTemplate_Level2">
<Label Content="{Binding}" Width="70" Height="70" HorizontalContentAlignment="Center">
</Label>
</DataTemplate>
<DataTemplate x:Key="DataTemplate_Level1">
<ItemsControl ItemsSource="{Binding}" ItemTemplate="{DynamicResource DataTemplate_Level2}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>
</Window.Resources>
I tried different ways using triggers but nothing seemed to Work.
Any help would be appreciated.
Thanks
It's as easy as this, but you'll be copying and pasting a lot of Setter tags. You might want to consider a value converter instead (see below).
<DataTemplate x:Key="DataTemplate_Level2">
<Grid
SnapsToDevicePixels="True"
x:Name="Background">
<Label Content="{Binding}" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding}" Value="1">
<Setter TargetName="Background" Property="Background" Value="Black" />
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="2">
<Setter TargetName="Background" Property="Background" Value="Khaki" />
</DataTrigger>
<DataTrigger Binding="{Binding}" Value="3">
<Setter TargetName="Background" Property="Background" Value="YellowGreen" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
And here's the value converter version:
<Window.Resources>
<local:ColorConverter x:Key="ColorConverter" />
<DataTemplate x:Key="DataTemplate_Level2">
<Grid
SnapsToDevicePixels="True"
Background="{Binding Converter={StaticResource ColorConverter}}">
<Label Content="{Binding}" />
</Grid>
</DataTemplate>
</Window.Resources>
C#
public class ColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Color clr = Colors.SteelBlue;
var s = value as String;
// Add code here to pick a color or generate RGB values for one
switch (s) {
case "1":
clr = Colors.Black;
break;
}
return new SolidColorBrush(clr);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Binding Converter doesn't apply on root TreeViewItem element (WPF)

I'm creating a file explorer app using the TreeView control provided by WPF. I've customized it such that it displays a file icon next to the path. I'm using a IValueConverter to convert the path to the required image, which I based off this page: http://www.codeproject.com/Articles/21248/A-Simple-WPF-Explorer-Tree
It mostly works! Except that the root TreeViewItems don't display icons for some reason. I placed a breakpoint on the IValueConverter::Convert() function and confirmed that it doesn't get executed for the root nodes, but do for all children nodes after that.
The VS output window shows no binding errors, so I'm at a loss at how this could happening. Any ideas?
Converter Code:
[ValueConversion(typeof(string), typeof(bool))]
public class HeaderToImageConverter : IValueConverter
{
public static HeaderToImageConverter Instance = new HeaderToImageConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if ((value as string).Contains(#"\"))
{
Uri uri = new Uri("pack://application:,,,/Images/diskdrive.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
else
{
Uri uri = new Uri("pack://application:,,,/Images/folder.png");
BitmapImage source = new BitmapImage(uri);
return source;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException("Cannot convert back");
}
}
TreeView XAML:
<TreeView Grid.Column="0" Name="fileExplorer" Margin="8,8,8,8">
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="20" Height="20" Stretch="Fill" Source="{Binding Converter={x:Static local:HeaderToImageConverter.Instance}}" />
<TextBlock Text="{Binding}" Margin="5,0" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
I ran into a similar problem. But besides the TreeView.Resources I had the TreeView.ItemContainerStyle element as well. Moving the Style element to there actually styled only the root, but not the children. Therefore, a solution would be to put the element in both places.
TreeView XAML:
<TreeView Grid.Column="0" Name="fileExplorer" Margin="8,8,8,8">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="20" Height="20" Stretch="Fill" Source="{Binding Converter={x:Static local:HeaderToImageConverter.Instance}}" />
<TextBlock Text="{Binding}" Margin="5,0" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.ItemContainerStyle>
<TreeView.Resources>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Width="20" Height="20" Stretch="Fill" Source="{Binding Converter={x:Static local:HeaderToImageConverter.Instance}}" />
<TextBlock Text="{Binding}" Margin="5,0" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</TreeView.Resources>
Now, it would be nicer not to have to reuse this HeaderTemplate Setter. But I have not found a way to do this.

How to change the font size in two different controls of WPF usercontrol?

I have Created and Implemented WPf UserContol like Window8 PasswordBox. When I Change the Font Both Textbox and Inner Button has Changed. Unfortunately Button Font size not suitable when textbox font size has perfect. (See the Image - Second Button has perfect button font size, but textbox has not. And third Button has not perfect, but textbox has perfect font size)
How Can I make to set two Controls font size while Implementation? Like the Property Button_fontSize and textbox_fontSize.
My Usercontol XAML Code:
<Grid.Resources>
<Style x:Key="ButtonWithoutHover" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border"
BorderThickness="3"
BorderBrush="White"
Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="Black" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Grid.Resources>
<Border BorderBrush="White" BorderThickness="2" >
<DockPanel Canvas.Right="2" Canvas.Top="2">
<Button Style="{StaticResource ButtonWithoutHover}" BorderThickness="3" BorderBrush="White" DockPanel.Dock="Right" Click="onButtonClick" >
<Button.Content>
<Label Content="->" Foreground="White" />
</Button.Content>
</Button>
<TextBox BorderThickness="0" Name="txtNumber" DockPanel.Dock="Left" >
</TextBox>
</DockPanel>
</Border>
Implementation XMAL Code:
<NumText:UserControl1 Click="UserControl1_Click" FontSize="9" Background="Red" Foreground="Yellow" Margin="160,46,206,229" />
<NumText:UserControl1 Click="UserControl1_Click" FontSize="20" Background="Red" Foreground="Yellow" Margin="121,104,173,145" />
<NumText:UserControl1 Background="Red" FontSize="36" Foreground="Yellow" Margin="121,180,173,69" />
One thing I've done in my work is create a Double resource to set the FontSize property. I believe it will solve your problem:
First, declare the namespace in your XAML file:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Then create two distinct Double "variables" with x:Key:
<sys:Double x:Key="SmallFont">10</sys:Double>
<sys:Double x:Key="LargeFont">35</sys:Double>
At last set your Control's FontSize property:
FontSize="{DynamicResource SmallFont}"
or
FontSize="{DynamicResource LargeFont}"
If I understood you correctly, this is how I've done! Hope it Helps.
You have overcomplicated the situation unnecessarily... you don't need different sized fonts, or DockPanels, or Borders. Instead of all this, just let the content size the UserControl, not the other way around. Try this simplified example (to use in the Usercontrol):
<Grid Margin="5">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="jagadees" BorderThickness="0"
VerticalAlignment="Center" />
<Button Grid.Column="1" Style="{StaticResource ButtonWithoutHover}"
Background="Red" Foreground="White" Content="->" VerticalAlignment="Center" />
</Grid>
UPDATE >>>
My whole point was that if you restructured your XAML then you would see that you didn't need to use different font sizes. However, if you are resolute that you do, then just implement a simple Converter class to change the size for one of the elements:
public class DoubleToLargerDoubleConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value.GetType() != typeof(double)) return false;
double amountToEnlargeBy = 0;
if (parameter == null || double.TryParse(parameter.ToString(), out amountToEnlargeBy)) return false;
double doubleValue = (double)value;
return doubleValue + amountToEnlargeBy;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return DependencyProperty.UnsetValue;
}
}
As you never actually said which text you wanted bigger, I'm just guessing that the Button text should be bigger. In this case, you'd use it like this:
<Label Content="->" Foreground="White" FontSize="{Binding FontSize, RelativeSource={
RelativeSource AncestorType={x:Type Button}}, Converter={StaticResource
DoubleToLargerDoubleConverter}, ConverterParameter=5.0}" />
I'm going to assume that you know how to use a Converter, so let me know if you don't. Just set the amount that you want the font size to be enlarged by in the
Binding.ConverterParameter property.

Categories

Resources