WPF Custom control binding image source - c#

I have a custom control based on a button, and I put an image inside. I can set the source of the image in the xaml, but if I try and bind it, it doesn't work.
Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControl">
<Style TargetType="{x:Type local:MyCustomControl}" BasedOn = "{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="pathname"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
This works just fine, however if I replace the <Image Source="pathname"/> with <Image Source={Binding MyImage, RelativeSource={RelativeSource Self}}"/>, and reference a Delegate Property in the class, it breaks.
MyCustomControl.cs
public class MyCustomControl : Button
{
static DependencyProperty m_myimage = null;
private DependencyProperty MyImageProperty
{
get
{
return m_myimage;
}
}
public BitmapImage MyImage
{
get
{
return (BitmapImage)GetValue(MyImageProperty);
}
set
{
SetValue(MyImageProperty, value);
}
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
MyImage = new BitmapImage(new Uri(pathname));
}
private static void RegisterDependencyProperties()
{
if (m_myimage == null)
{
m_myimage = DependencyProperty.Register("MyImage",
typeof(BitmapImage), typeof(MyCustomControl), null);
}
}
static MyCustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl),
new FrameworkPropertyMetadata(typeof(MyCustomControl)));
RegisterDependencyProperties();
}
}
How can I get it to work?

Figured it out myself after about 2 hours. The xaml binding should look like,
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyCustomControl}">
<Grid x:Name="InnerGrid">
<Image Source="{Binding MyImage, RelativeSource={RelativeSource TemplatedParent}}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
Note that the binding relative resource went from RelativeSource={RelativeSource Self} to RelativeSource={RelativeSource TemplatedParent}

Related

Pass custom properties in WPF templates

I'm trying to create a menu that works with radio buttons. The buttons are graphically prettied by a template. here I would like to display an icon and a text. However, I don't know how I can pass several parameters, so far I only pass the text and have not yet found a way to pass the image.
<StackPanel Grid.Row="1" Margin="0,10,0,0">
<RadioButton Content="Dashboard"
IsChecked="True"
Style="{StaticResource MenuButtonTheme}"/>
<RadioButton Content="Product"
Style="{StaticResource MenuButtonTheme}"/>
<RadioButton Content="Inventory"
Style="{StaticResource MenuButtonTheme}"/>
</StackPanel>
Style Of the Radiobutton
<Style BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="{x:Type RadioButton}"
x:Key="MenuButtonTheme">
<Style.Setters>
<Setter Property="Foreground" Value="#FFFFFF"/>
<Setter Property="FontFamily" Value="/Fonts/#Poppins"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Border Background="{TemplateBinding Background}"
CornerRadius="5"
Margin="5,0,5,0">
<Grid VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Height="50"
>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<iconPacks:PackIconBoxIcons Kind="SolidPieChartAlt2"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="10,0,0,0"/>
<TextBlock Grid.Column="1" Text="{TemplateBinding Property=Content}"
VerticalAlignment="Center"
FontSize="20"
FontWeight="Regular"
Margin="10,0,0,0"/>
</Grid>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Background" Value="#212121"/>
<Setter Property="Foreground" Value="#4169E1"/>
</Trigger>
</Style.Triggers>
</Style>
Foreach particular part of UI in your application, I recommend you to make it a module, that is, a UserContol or ContentControl(recommened). These controls corresponds to View in MVVM, and foreach of them you should add a View Model.
namespace MyNameSpace{
public class View<T> : ContentControl {
public T ViewModel {
get { return (T)GetValue(ViewModelProperty); }
set { SetValue(ViewModelProperty, value); }
}
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register("ViewModel", typeof(T), typeof(View<T>), new PropertyMetadata());
}
public abstract class ViewModel : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged([CallerMemberName] String propertyName = null) {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
If the relative logic is purely UI, then Model is not needed in this case.
Your View's xaml should look like this:
<local:View x:TypeArguments="MyAViewModel" x:Name="view"
x:Class="MyNameSpace.MyAView"
skip
xmlns:local="clr-namespace:MyNameSpace">
<Image Source="{Binding ViewModel.ImageSource,ElementName=view}"/>
</local:View>
Your ViewModel should look like this:
public class MyAViewModel: ViewModel {
public AbilityViewModel() {//Constructor with parameter
//Set the image source here
}
private ImageSource imageSource;
public ImageSource ImageSource{
get => imageSource
set{
imageSource = value;
NotifyPropertyChanged();
}
}
}
In the root element of your UI hierarchy, for example your MainWindow, add your custom contols:
<Window x:Name="window" skip>
<Grid>
<local:MyAView ViewModel="{Binding MyAViewModel,ElementName=window}"/>
<local:MyBView ViewModel="{Binding MyBViewModel,ElementName=window}"/>
</Grid>
</Window>
You may either do so with adding dependency properies of the MyAViewModel and MyBViewModel to your MainWindow, or just set MyAView's ViewModel in MainWindow's constructor or loaded event. You may create the ViewModel to pass to view, in which ImageSource is initialized in constructor, or change it after its construction by somewhere in your code.
Above codes are just demo, directly written in stackoverflow's webpage and is not tested. You may ask me if there is any problem.

How to draw grid lines that scale?

I have a canvas, and I want to give it grid lines as a background, but I want there to be a constant number of grid lines that divide the canvas into equally-sized sections, rather than just have equally-spaced grid-lines. I want this to be preserved when the canvas is resized by the user.
How should I do this?
Here is a solution which is based in two wpf ListView controls behind the canvas(one for rows and second for columns). The content of the columns related ListView control is a rectangle.
Updated version - Managed Grid Lines Control. Here you can manage the number of grid lines and their visibility.
Xaml code - grid lines control:
<UserControl x:Class="CAnvasWithGrid.GridLineControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:canvasWithGrid="clr-namespace:CAnvasWithGrid"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="This">
<Grid x:Name="LayoutRoot">
<Grid.Resources>
<Style TargetType="ListView">
<Setter Property="Background" Value="Transparent"/>
</Style>
<Style x:Key="ListViewItemStyle" TargetType="ListViewItem">
<Setter Property="Background" Value="Transparent"/>
</Style>
<DataTemplate x:Key="InnerListviewDataTemplate" DataType="{x:Type canvasWithGrid:CellModel}">
<Rectangle HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Margin="0" StrokeDashArray="4" Stroke="Black" StrokeThickness="0.5" Fill="Transparent"/>
</DataTemplate>
<DataTemplate x:Key="ListviewDataTemplate" DataType="{x:Type canvasWithGrid:RowModel}">
<ListView ItemsSource="{Binding CellModels}" BorderBrush="#00FFFFFF" BorderThickness="0" Margin="0"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding CellModels, Converter={canvasWithGrid:CollectionLength2NumberConverter}}"></UniformGrid>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemStyle}">
<Setter Property="Margin" Value="0"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter Content="{TemplateBinding Content}" Margin="0"
ContentTemplate="{StaticResource InnerListviewDataTemplate}" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate" Value="{StaticResource InnerListviewDataTemplate}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</DataTemplate>
</Grid.Resources>
<ListView ItemsSource="{Binding ElementName=This, Path=RowModels}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="{Binding ElementName=This, Path=RowModels, Converter={canvasWithGrid:CollectionLength2NumberConverter}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem" BasedOn="{StaticResource ListViewItemStyle}">
<Setter Property="Margin" Value="0"></Setter>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter Content="{TemplateBinding Content}" Margin="-1"
ContentTemplate="{StaticResource ListviewDataTemplate}" />
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate" Value="{StaticResource ListviewDataTemplate}"/>
</Style>
</ListView.ItemContainerStyle>
</ListView>
</Grid>
Grid lines control - code behind
/// <summary>
/// Interaction logic for GridLineControl.xaml
/// </summary>
public partial class GridLineControl : UserControl
{
public GridLineControl()
{
InitializeComponent();
}
public static readonly DependencyProperty NumberOfColumnsProperty = DependencyProperty.Register(
"NumberOfColumns", typeof (int), typeof (GridLineControl), new PropertyMetadata(default(int), NumberOfColumnsChangedCallback));
private static void NumberOfColumnsChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var numberOfRows = (int)dependencyObject.GetValue(NumberOfRowsProperty);
var numberOfColumns = (int)args.NewValue;
if (numberOfColumns == 0 || numberOfRows == 0) return;
var rowModelsCollection = GetRowModelsCollection(numberOfRows, numberOfColumns);
dependencyObject.SetValue(RowModelsProperty, rowModelsCollection);
}
public int NumberOfColumns
{
get { return (int) GetValue(NumberOfColumnsProperty); }
set { SetValue(NumberOfColumnsProperty, value); }
}
public static readonly DependencyProperty NumberOfRowsProperty = DependencyProperty.Register(
"NumberOfRows", typeof (int), typeof (GridLineControl), new PropertyMetadata(default(int), NumberOfRowsChangedCallback));
private static void NumberOfRowsChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
{
var numberOfRows = (int)args.NewValue;
var numberOfColumns = (int)dependencyObject.GetValue(NumberOfColumnsProperty);
if(numberOfColumns == 0 || numberOfRows == 0) return;
var rowModelsCollection = GetRowModelsCollection(numberOfRows, numberOfColumns);
dependencyObject.SetValue(RowModelsProperty, rowModelsCollection);
}
private static ObservableCollection<RowModel> GetRowModelsCollection(int numberOfRows, int numberOfColumns)
{
var rowModelsCollection = new ObservableCollection<RowModel>();
for (var i = 0; i < numberOfRows; i++)
{
rowModelsCollection.Add(new RowModel(numberOfColumns) {Position = (i + 1).ToString()});
}
return rowModelsCollection;
}
public int NumberOfRows
{
get { return (int) GetValue(NumberOfRowsProperty); }
set { SetValue(NumberOfRowsProperty, value); }
}
public static readonly DependencyProperty RowModelsProperty = DependencyProperty.Register("RowModels",
typeof(ObservableCollection<RowModel>), typeof(GridLineControl),
new PropertyMetadata(default(ObservableCollection<RowModel>)));
public ObservableCollection<RowModel> RowModels
{
get { return (ObservableCollection<RowModel>)GetValue(RowModelsProperty); }
private set { SetValue(RowModelsProperty, value); }
}
}
Models:
public class RowModel:BaseGridMember
{
public RowModel(int numberOfCellsInRow)
{
CellModels = new ObservableCollection<CellModel>();
for (int i = 0; i < numberOfCellsInRow; i++)
{
CellModels.Add(new CellModel{Position = (i+1).ToString()});
}
}
public ObservableCollection<CellModel> CellModels { get; set; }
}
public class CellModel:BaseGridMember
{
}
public class BaseGridMember:BaseObservableObject
{
private string _position;
public string Position
{
get { return _position; }
set
{
_position = value;
OnPropertyChanged();
}
}
}
Main window xaml code - as you can see here is a ImageContol instead of Canvas but you can replace it:
<Window x:Class="CAnvasWithGrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:canvasWithGrid="clr-namespace:CAnvasWithGrid"
Title="MainWindow" Height="525" Width="525" x:Name="This">
<Grid Tag="{Binding ElementName=This}">
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="Bool2VisConvKey" />
</Grid.Resources>
<Grid.ContextMenu>
<ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Show Grid Lines" Command="{Binding ShowGridLinesCommand}"/>
</ContextMenu>
</Grid.ContextMenu>
<Image Source="Resources/Koala.jpg" Stretch="Uniform" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MouseDown="UIElement_OnMouseDown"/>
<canvasWithGrid:GridLineControl NumberOfRows="50" NumberOfColumns="50"
IsHitTestVisible="False" Visibility="{Binding ElementName=This, Path=AreGridLineVisible, Converter={StaticResource Bool2VisConvKey}, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
Main window code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ShowGridLinesCommand = new RelayCommand(ShowGridLineManageCommand);
AreGridLineVisible = true;
}
private void ShowGridLineManageCommand()
{
AreGridLineVisible = !AreGridLineVisible;
}
public static readonly DependencyProperty AreGridLineVisibleProperty = DependencyProperty.Register(
"AreGridLineVisible", typeof (bool), typeof (MainWindow), new PropertyMetadata(default(bool)));
public bool AreGridLineVisible
{
get { return (bool) GetValue(AreGridLineVisibleProperty); }
set { SetValue(AreGridLineVisibleProperty, value); }
}
public static readonly DependencyProperty ShowGridLinesCommandProperty = DependencyProperty.Register(
"ShowGridLinesCommand", typeof (ICommand), typeof (MainWindow), new PropertyMetadata(default(ICommand)));
public ICommand ShowGridLinesCommand
{
get { return (ICommand) GetValue(ShowGridLinesCommandProperty); }
set { SetValue(ShowGridLinesCommandProperty, value); }
}
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
}
}
How it looks like:
Sounds like a candidate for a custom control with custom drawing. You don't really want to use multiple FrameworkElements like "Line" if you are expecting many grid-lines for performance reasons.
So you'd create a customControl GridLinesControl and overwrite the OnRender method. You can get the actual width and height of the component using the properties ActualWidth and ActualHeight, divide by the number of grid lines you want and draw lines using drawingContext.DrawLine.
The easiest way would be to add the GridLinesControl you've made underneath the canvas, taking up the same space (so it has the right ActualWidth and ActualHeight) like this:
<Grid>
<myControls:GridLinesControl/>
<Canvas ... />
</Grid>
So it's always underneath.

DataGridTemplateColumn as custom control

I try to make custom control based on DataGridTemplateColumn for using it in simple datagrid.
My XAML code looks:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dgNuCol="clr-namespace:Columns.DataGridNumberColumn"
xmlns:nb="clr-namespace:NumberBox" >
<Style TargetType="{x:Type dgNuCol:DataGridNumberColumn}">
<Setter Property="CellEditingTemplate">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dgNuCol:DataGridNumberColumn}">
<nb:NumberBox Text="123"/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="CellTemplate">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dgNuCol:DataGridNumberColumn}">
<StackPanel>
<TextBlock Text="123" HorizontalAlignment="Center" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And my cs code:
namespace Columns.DataGridNumberColumn
{
public partial class DataGridNumberColumn : DataGridTemplateColumn
{
}
}
And I try to use it in my datagrid:
<dgNuCol:DataGridNumberColumn Header="Values" CanUserReorder="False"/>
Nothing happens. There are no any changes in my datagrid, just empty cell. What I am doing wrong?
UPDATE:
I try to make it programmatically:
public partial class DataGridNumberColumn : DataGridTemplateColumn
{
public DataGridNumberColumn()
{
FrameworkElementFactory textFactory = new FrameworkElementFactory(typeof(TextBox));
textFactory.SetValue(TextBox.TextProperty, "123");
DataTemplate textTemplate = new DataTemplate();
textTemplate.VisualTree = textFactory;
this.CellTemplate = textTemplate;
}
}
But, again, no any changes in datagrid.
UPDATE2:
<DataGrid>
<DataGrid.Columns>
<dgNuCol:DataGridNumberColumn Header="Values" CanUserReorder="False"/>
</DataGrid.Columns>
</DataGrid>

WPF Custom Control - Button Event

I start the WPF today, I'm trying to implement a custom control.
My problem is that I can not select an element in the template.
My code:
[Generic.xaml]
<Style x:Key="{x:Type local:ImageButton}" TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<StackPanel>
// -> My image
<Image Source="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}"></Image>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
[ImageButton.cs]
public class ImageButton : Button
{
public Image imageOff { get; set; }
public Image imageOn { get; set; }
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
public ImageButton()
: base()
{
this.MouseEnter += new MouseEventHandler(SetImageON);
}
public void SetImageON(object sender, MouseEventArgs e)
{
//Here i wanna change my image from StackPanel
}
}
Am I on the good way ? How can I change that image?
Give a name to your image, like x:Name="PART_Image". Then in your code behind override OnApplyTemplate method and store it into a variable.
private Image PART_Image;
public override void OnApplyTemplate()
{
PART_Image = this.GetTemplatedChild("PART_Image");
}
In this case you can access it in your method like:
this.PART_Image.Visibility = Visibility.Collapsed;
sender is your control so you could also right this.SetCurrentValue.
public void SetImageON(object sender, MouseEventArgs e)
{
ImageButton btn = (ImageButton)sender;
btn.SetCurrentValue(TagProperty,imageOn.Source);
}
it seems as though you might wan't to just save the source of your image in the control , instead of the entire image.
But if you wan't to use an Image object in code like this
xaml : in your template .
<StackPanel x:Name="myPanel" />
cs : in your control :
myPanel.Children.Add(imageOn);
You can achieve what you want with a Trigger:
<Style x:Key="{x:Type local:ImageButton}" TargetType="{x:Type local:ImageButton}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ImageButton}">
<StackPanel>
<Image Source="{Binding Path=imageOff.Source, RelativeSource={RelativeSource TemplatedParent}}">
<Image.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Image.Source" Value="{Binding Path=imageOn.Source, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</Image.Triggers>
</Image>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
That way you don't need the event in the code-behind. If you wanted to, you could make imageOff and imageOn DependencyProperties, that way you could define the source images in xaml too:
public class ImageButton : Button {
public static readonly DependencyProperty imageOffProperty = DependencyProperty.Register("imageOff", typeof(Image), typeof(ImageButton), new PropertyMetadata(null));
public Image imageOff {
get { return (Image)GetValue(imageOffProperty); }
set { SetValue(imageOffProperty, value); }
}
public static readonly DependencyProperty imageOnProperty = DependencyProperty.Register("imageOn", typeof(Image), typeof(ImageButton), new PropertyMetadata(null));
public Image imageOn {
get { return (Image)GetValue(imageOnProperty); }
set { SetValue(imageOnProperty, value); }
}
static ImageButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageButton), new FrameworkPropertyMetadata(typeof(ImageButton)));
}
}
So when you declare an ImageButton in xaml, you can do something like this:
<local:ImageButton imageOn="/Resource/Path/To/imageOn.png" imageOff="/Resource/Path/To/imageOff.png" />

Optional Visibiltiy for Button in ControlTemplate

I have the following style for a Tabitem which contains a close button.
<Style x:Key="StudioTabItem" TargetType="{x:Type TabItem}">
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
...
<Button Grid.Column="2"
Width="15"
Height="15"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Visibility={Binding}>
...
I would like to make the visibility of the StudioTabItems button optional when I use the actual control. So something like
<TabControl x:Name="tabControl"
Style="{StaticResource StudioTabControl}"
ItemsSource="{Binding Workspaces}"
SelectedIndex="{Binding SelectedIndex}"
TabStripPlacement="Top" >
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem"
BasedOn="{StaticResource StudioTabItem}"
IsCloseButtonVisible="False"> <-- How to do this?
See the IsCloseButtonVisible on the last line of the above. I know this is likely to involve DependencyProperties. Is this possible and how can I achieve this?
Thanks for your time.
This can be achieved by creating the Attached Property like below and by setting its property in style setter
public static class TabItemBehaviour
{
public static readonly DependencyProperty IsCloseButtonVisibleProperty =
DependencyProperty.RegisterAttached("IsCloseButtonVisible", typeof(bool), typeof(TabItemBehaviour), new UIPropertyMetadata(true, IsButtonVisiblePropertyChanged));
public static bool GetIsCloseButtonVisible(DependencyObject obj)
{
return (bool)obj.GetValue(IsCloseButtonVisibleProperty);
}
public static void SetIsCloseButtonVisible(DependencyObject obj, bool value)
{
obj.SetValue(IsCloseButtonVisibleProperty, value);
}
public static void IsButtonVisiblePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
TabItem item = o as TabItem;
if (item != null)
{
Button closeButton = item.Template.FindName("CloseButton", item) as Button;
if ((bool)e.NewValue == true)
{
closeButton.Visibility = Visibility.Visible;
}
else
{
closeButton.Visibility = Visibility.Collapsed;
}
}
}
}
And then TabItem style just set the property:
<Style TargetType="TabItem"
BasedOn="{StaticResource StudioTabItem}"
>
<Setter Property="behaviours:TabItemBehaviour.IsCloseButtonVisible" Value="False"/>
Also you will have to give Button a Name "CloseButton" in your ControlTemplate

Categories

Resources