Pin Toggle button style - wpf

Hi i want to create a generic style for pin button.
<Window x:Class="TooglePinButtonStyle.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>
<Image x:Key="pinImage"
Width="14"
Height="14"
Source="/TooglePinButtonStyle;component/Images/pin.png" />
<Image x:Key="unPinImage"
Width="14"
Height="14"
Source="/TooglePinButtonStyle;component/Images/unpin.png" />
<Style x:Key="pinButtonStyle"
TargetType="{x:Type ToggleButton}">
<Setter Property="Content" Value="{DynamicResource unPinImage}" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource pinImage}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<ToggleButton Height="30"
Width="30"
Style="{StaticResource pinButtonStyle}"/>
<ToggleButton Height="30"
Width="30"
Style="{StaticResource pinButtonStyle}"/>
</StackPanel>
</Window>
It works fine when there is only one button but when I have two button the UI crashes with
"Specified element is already the logical child of another element.
Disconnect it first."
exception.

Either make the images non-shared, or set the ContentTemplate to some DataTemplate which contains an image (not a reference to an image), rather than the Content. If you have only one instance of an UI-element you will run into this problem, templates describe what should be created rathen than using instances directly.

Related

Add name to specific style control wpf

I have a custom contextmenu:
<Window.Resources>
<ContextMenu x:Key="RowMenu" DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<ContextMenu.Style>
<Style TargetType="ContextMenu">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="Transparent">
<Border Background="#1c1c1c" Height="70" Width="170" CornerRadius="10">
<StackPanel Orientation="Vertical">
<Button x:Name="openinBrowser" Click="Button_Click_1">
<Grid Width="170">
<materialDesign:PackIcon Kind="OpenInApp" VerticalAlignment="Center" Foreground="{StaticResource PrimaryHueMidBrush}" HorizontalAlignment="Left"/>
<Label FontFamily="Champagne & Limousines" Content="Action 1" FontSize="7" HorizontalAlignment="Center" Foreground="LightGray" VerticalAlignment="Center"/>
</Grid>
<Button.Style>
<Style BasedOn="{StaticResource MaterialDesignRaisedAccentButton}" TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="{StaticResource PrimaryHueMidBrush}"/>
<Setter Property="BorderThickness" Value="0"/>
</Style>
</Button.Style>
</Button>
</StackPanel>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ContextMenu.Style>
</ContextMenu>
</Window.Resources>
How would I be able to add a name to the Button so I can enable and disable it in my c# (without using binding), I have tried putting x:Name="" but it doesn't work, but if I add a button click it works? I am quite confused, any help would be appreciated!
I still say you should be doing this properly with data-binding, but if you insist...there are a couple of different ways to go about this.
Context menus aren't part of the regular visual tree, so you have to access them directly. Give your context menu a name, and then find the button by traversing its template's visual tree:
// button has to be templated in order for this to work,
// so don't try it in the parent window's constructor
// (add a this.contextMenu.Loaded handler instead if you have to)
var button = this.contextMenu.Template.FindName("openinBrowser", this.contextMenu) as Button;
If your visual tree is particularly complex then a faster option would be to create a boolean resource in your window's resources block:
xmlns:sys="clr-namespace:System;assembly=mscorlib"
<Window.Resources>
<sys:Boolean x:Key="ButtonEnabled">True</sys:Boolean>
</Window.Resources>
...and then bind your button to that dynamically:
<Button x:Name="openinBrowser" IsEnabled="{DynamicResource ButtonEnabled}">
This breaks your "no binding" rule though, which is why I was asking why you're so adamant about not using data-binding...you can still use it, even if you're not binding to the data context. In this scenario you set the value of that resource in your code instead:
this.Resources["ButtonEnabled"] = false;

Mouseover border in a custom control for a textblock

I am trying to create a custom control for a text block that when moused over, a border will appear. I am pretty new to WPF and have only made some very simple custom controls. I need to implement this in a XAML UserControl.
Any suggestions would be greatly appreciated. Thanks again, StackOverflow.
EDIT: I am going to have to bind a persistence property to several different controls, so I really need to do this in a custom control. This is what I have, and it isn't working:
xmlns:customControls="clr-namespace:****.CustomControls"
....
<customControls:MouseOverBorder>
<TextBlock Style="{StaticResource ResourceKey=HomePageButtonText}"
Height="100"
Width="100"
Margin="5"
Text="View Reports" />
</customControls:MouseOverBorder>
And the UserControl:
<UserControl
x:Class="****.MouseOverBorder"
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"
mc:Ignorable="d">
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="MouseOverBorder" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="3" />
<Style.Triggers>
<Trigger Property="Border.IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Border Style="{DynamicResource MouseOverBorder}" BorderThickness="1" CornerRadius="3" SnapsToDevicePixels="True"/>
No need to make a UserControl. I've managed to accomplish this with the following markup:
<Border Style="{DynamicResource BorderStyle1}" BorderThickness="1" CornerRadius="3" >
<TextBlock Text="TextBlock" />
</Border>
Here's the style:
<Style x:Key="BorderStyle1" TargetType="{x:Type Border}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="CornerRadius" Value="3"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="#FF123BBA"/>
</Trigger>
</Style.Triggers>
</Style>
EDIT:
Still don't get it why do you need a UserControl (please don't call it custom control - these are different things), but let's consider your example.
When you write the following
<customControls:MouseOverBorder>
<TextBlock Style="{StaticResource ResourceKey=HomePageButtonText}"
Height="100"
Width="100"
Margin="5"
Text="View Reports" />
</customControls:MouseOverBorder>
you are actually setting MouseOverBorder.Content property. Originally it's Content is defined in MouseOverBorder.xaml file. So you are replacing all your UserControl structure with TextBlock. But still I got your idea and have solution for it.
First, add custom DependencyProperty and CLR wrapper for it to MouseOverBorder class:
public static readonly DependencyProperty MyContentTemplateProperty =
DependencyProperty.Register("MyContentTemplate", typeof(DataTemplate), typeof(MouseOverBorder), null);
[Browsable(true)]
[Category("Other")]
public DataTemplate MyContentTemplate
{
get { return (DataTemplate)GetValue(MyContentTemplateProperty); }
set { SetValue(MyContentTemplateProperty, value); }
}
Second, make something inside MouseOverBorder use this property, e.g.
<ContentPresenter ContentTemplate="{Binding MyContentTemplate, ElementName=userControl}"/>
<!-- userControl is the Name of MouseOverBorder, defined in xaml -->
At last, you can use your UserControl as following:
<customControls:MouseOverBorder>
<customControls:MouseOverBorder.MyContentTemplate>
<DataTemplate>
<TextBlock Style="{StaticResource ResourceKey=HomePageButtonText}"
Height="100"
Width="100"
Margin="5"
Text="View Reports" />
</DataTemplate>
</customControls:MouseOverBorder.MyContentTemplate>
</customControls:MouseOverBorder>

Parametrized Style within UserControl?

I used this MSDN tutorial to create an eye candy look for all the Button controls of my window, and that worked fine.
To make it even more reusable, I tried to put all in a UserControl: I created a ImageButton UC, then I encapsulated all that <Style> from <Window.Resources> to <UserControl.Resources>.
Then I changed my Button instances in XAML, from:
<Button Tag="Face.jpg" Content="Foo" />
To:
<uc:ImageButton Tag="Face.jpg" Content="Foo" />
And the style stopped being applied.
Here's the UC code:
<UserControl x:Class="GDTI.UI.Main.View.UserControls.ImageButton"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Style TargetType="Button">
<Setter Property="MaxWidth" Value="250" />
<Setter Property="Margin" Value="5" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" >
<Setter.Value>
<SolidColorBrush Color="Orange" Opacity="0.4" />
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate >
<StackPanel>
<Image Source="{Binding Tag,
RelativeSource={RelativeSource
FindAncestor,
AncestorType='Button'}}" />
<TextBlock Margin="10"
HorizontalAlignment="Center"
Text="{Binding Content,
RelativeSource={RelativeSource
FindAncestor,
AncestorType='Button'}}" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Button/>
What am I missing?
Thank you!
The bindings in the button-style target properties on the button, which no longer has the properties set. You need to forward those to the UserControl if you want to retain the style's integrity:
<!-- Inside UserControl declaration -->
<Button Content="{Binding Caption, RelativeSource={RelativeSource AncestorType=UserControl}}"
Tag="{Binding ImageSource, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
Where Caption and ImageSource should be new dependency properties defined on the UserControl (in code-behind).
Note that you can never bind to Content in a UserControl (hence the Caption property), here the Button itself is the Content of the UserControl.
Alternatively you could directly change the targeting in the style by changing the AncestorType to UserControl which bypasses the Button. Binding beyond the templated control is not exactly good practice but your are still inside the UserControl so it may be forgivable.
Either way this is a bit hacky and it might be better to inherit from Button instead.

Setting Button's Content to <Image> via Styles

Can't get this to work:
<UserControl>
<UserControl.Resources>
<ResourceDictionary>
<Style x:Key="TestStyle" TargetType="{x:Type Button}">
<Setter Property="Button.Content">
<Setter.Value>
<Image Source="D:\Temp\dictionary16.png"/>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<Button Style="{StaticResource TestStyle}"/>
<Button Style="{StaticResource TestStyle}"/>
</StackPanel>
</UserControl>
This code throws the following exception (pointing to the second button):
Specified element is already the logical child of another element. Disconnect it first.
The style creates one instance of the Image, you cannot use it in two places like this. You can create the image as a separate resource with x:Shared= false and reference it in the style then a new one will be created in every place the style is used.
e.g.
<UserControl>
<UserControl.Resources>
<Image x:Key="img" x:Shared="false" Source="D:\Temp\dictionary16.png" />
<Style x:Key="TestStyle" TargetType="{x:Type Button}">
<Setter Property="Content" Value="{StaticResource img}" />
</Style>
</UserControl.Resources>
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<Button Style="{StaticResource TestStyle}" />
<Button Style="{StaticResource TestStyle}" />
</StackPanel>
</UserControl>
Already yesterday i found a user with a similar problem: WPF - Change a button's content in a style?
This post got me to this soloution (couldn't post it because of 8 hour limit of stackoverflow -.-)
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{mcWPF:LangRes imgSettings16, Bitmap}" Height="14"/>
</DataTemplate>
</Setter.Value>
</Setter>
Don't know weather this is more clean/dirty/better than H.B.'s soloution

Constraining item heights in WPF ListBox, with indicator

I have a ListBox control in WPF which contains items of variable height (predominantly a large text block, so it's also affected by word wrapping). Since scrolling behaves badly when the height of an individual item gets too high (especially when close to the height of the ListBox itself), I want to constrain the max height of the individual items.
I've done that readily enough, by using a Style to set the MaxHeight of the ListBoxItem container.
My problem is that I would like to detect that an individual item has hit that constraint, and then style it differently.
This was my first attempt:
<Style x:Key="LogContainerStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="MaxHeight" Value="64" />
<EventSetter Event="MouseDoubleClick" Handler="LogEntry_MouseDoubleClick" />
</Style>
<DataTemplate x:Key="LogTemplate">
<Grid>
<TextBlock Text="{Binding Message}" />
<TextBlock x:Name="More" Text="(more)"
HorizontalAlignment="Right" VerticalAlignment="Bottom"
Foreground="DarkGray" Visibility="Collapsed" />
</Grid>
<DataTemplate.Triggers>
<Trigger ... height capped at MaxHeight? ...>
<Setter TargetName="More" Property="Visibility" Value="Visible" />
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
But I'm not sure how to write the trigger. Alternatives welcome.
Try the code below. I set the ListBoxItem.MaxHeight to 99. I then added a trigger in the DataTemplate that checks the ActualHeight of the root element in the template (i.e. "bd" below) and if it's 99, I change the BorderBrush. Hope this helps.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
ShowActivated="False"
Title="MainWindow" Height="350" Width="525">
<ListBox x:Name="lb">
<ListBox.ItemsSource>
<x:Array Type="{x:Type sys:Double}">
<sys:Double>250</sys:Double>
<sys:Double>100</sys:Double>
<sys:Double>50</sys:Double>
<sys:Double>25</sys:Double>
<sys:Double>99</sys:Double>
<sys:Double>120</sys:Double>
</x:Array>
</ListBox.ItemsSource>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="MaxHeight" Value="99"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border x:Name="bd" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding}" Height="{Binding}" Background="LightGray"/>
</Border>
<DataTemplate.Triggers>
<Trigger SourceName="bd" Property="ActualHeight" Value="99">
<Setter TargetName="bd" Property="BorderBrush" Value="Red"/>
</Trigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>

Resources