How to bind custom dependency property in control template? - wpf

I have one class ChromeWindow which is derived from Window class. It has one custom dependency property
public class ChromeWindow : System.Windows.Window
{
public static readonly DependencyProperty TitleBarHeightProperty = DependencyProperty.Register("TitleBarHeight", typeof(int), typeof(ChromeWindow), new FrameworkPropertyMetadata(TitleBarHeightChangedCallback));
public int TitleBarHeight
{
get
{
return (int)GetValue(TitleBarHeightProperty);
}
set
{
SetValue(TitleBarHeightProperty, value);
}
}
private static void TitleBarHeightChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
}
And ChromeWindow has its own control template as below.
<ControlTemplate x:Key="ChromeWindowTemplate" TargetType="local:ChromeWindow">
<Grid>
<AdornerDecorator>
<ContentPresenter />
</AdornerDecorator>
<StackPanel x:Name="PART_TitleBar"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Background="Transparent"
Height="?">
<StackPanel HorizontalAlignment="Right"
VerticalAlignment="Top"
Orientation="Horizontal">
<Button x:Name="PART_MinimizeButton"
Width="34"
Height="26"
Style="{DynamicResource SystemButton}"
ToolTip="Minimize">
<Path Width="8"
Height="8"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="F1M0,6L0,9 9,9 9,6 0,6z"
Fill="{Binding Foreground,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Button}}" />
</Button>
<Button x:Name="PART_MaximizeButton"
Width="34"
Height="26"
Style="{DynamicResource SystemButton}"
ToolTip="Maximize">
<Path x:Name="PART_MaximizeButtonPath"
Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="F1M0,0L0,9 9,9 9,0 0,0 0,3 8,3 8,8 1,8 1,3z"
Fill="{Binding Foreground,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Button}}" />
</Button>
<Button x:Name="PART_CloseButton"
Width="34"
Height="26"
Style="{DynamicResource SystemButton}"
ToolTip="Close">
<Path Width="10"
Height="8"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="F1M0,0L2,0 5,3 8,0 10,0 6,4 10,8 8,8 5,5 2,8 0,8 4,4 0,0z"
Fill="{Binding Foreground,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=Button}}" />
</Button>
</StackPanel>
</StackPanel>
<ResizeGrip x:Name="PART_ResizeGrip"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Cursor="SizeNWSE"
Visibility="Hidden" />
</Grid>
</ControlTemplate>
How can I bind PART_Titlebar Height to custom dependency property called "TitleBarHeight" such that
If TitleBarHeight value is not specified, title bar height should
be calculated according to its child contents ?
If TitleBarHeight value is specified, use that value.
Thanks in advance.

The default value of the Height property is double.NaN, which indicates that there is no explicitly set height.
So you should change the type of your dependency property to double and set double.NaN as its default value:
public static readonly DependencyProperty TitleBarHeightProperty =
DependencyProperty.Register(
nameof(TitleBarHeight), typeof(double), typeof(ChromeWindow),
new FrameworkPropertyMetadata(double.NaN));
public double TitleBarHeight
{
get { return (double)GetValue(TitleBarHeightProperty); }
set { SetValue(TitleBarHeightProperty, value); }
}

Related

WPF controls not showing even make it visible

I encounter a problem that I am not sure how to fix. I have "AddAccount" (Button), "AccountsTextBlock" and a ListBox on the main window. If not user is added, the list box will be hidden, once user is added, it will show the ListBox, AddAccount and AccountsTextBlock on the same screen, but AddAccount and AccountsTextBlock will be at different positions from when there's no user. The problem is that AddAccount and AccountsTextBlock didn't show, but a dotted rectangle shows.
<Grid>
<Canvas>
<TextBlock x:Name="AddAccountTextBlock" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top" Height="60" Width="369" Text="Add account to allow for simple access to your company resources
" SnapsToDevicePixels="True" Canvas.Left="38" Canvas.Top="313"/>
<Button x:Name="AddAccount" Content="Add Account" Canvas.Left="145" Canvas.Top="333" HorizontalAlignment="Left" VerticalAlignment="Top" Width="140" RenderTransformOrigin="0.466,0.977" IsCancel="True" Height="40" Foreground="White" Background="Blue" Click="OnAddAccount"/>
<TextBlock x:Name="AccountsTextBlock" Canvas.Left="176" TextWrapping="Wrap" Text="Accounts" Canvas.Top="10" Height="33" Width="68" FontSize="16" FontFamily="Tahoma"/>
</Canvas>
<ListBox x:Name="accounts" HorizontalAlignment="Left" Height="237" VerticalAlignment="Top" Width="444" SelectionChanged="ListBox_SelectionChanged" Background="White" BorderBrush="White" Margin="0,47,0,0" RenderTransformOrigin="0.5,0.5">
<ListBox.RenderTransform>
<TransformGroup>
<ScaleTransform/>
<SkewTransform AngleX="0.384"/>
<RotateTransform/>
<TranslateTransform X="-0.794"/>
</TransformGroup>
</ListBox.RenderTransform>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Height" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="_Border" BorderBrush="Gray" BorderThickness="0.5"
Padding="2"
SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="_Border" Property="Background" Value="LightSkyBlue"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Margin="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image HorizontalAlignment="Left" VerticalAlignment="Center" Grid.Row="0" Grid.Column="0" Width="100" Source="{Binding Path=imagePath}" />
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="0" Grid.Column="1" Text="{Binding Path=userInfo}"></TextBlock>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
public MainWindow()
{
InitializeComponent();
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(authenticatorRegistryKey))
{
Object valueStr = Registry.GetValue(authenticatorRegistryKey, addedUsersRegistryValueName, null);
if (valueStr == null)
{
//no users added yet, hide list box and move add button
accounts.Visibility = Visibility.Collapsed;
AccountsTextBlock.Visibility = Visibility.Collapsed;
Canvas.SetLeft(AddAccountTextBlock, (Application.Current.MainWindow.Width - AddAccountTextBlock.Width) / 2);
Canvas.SetTop(AddAccountTextBlock, (Application.Current.MainWindow.Height - AddAccountTextBlock.Height) / 2 - AddAccount.Height + 10);
Canvas.SetLeft(AddAccount, (Application.Current.MainWindow.Width - AddAccount.Width) / 2);
Canvas.SetTop(AddAccount, (Application.Current.MainWindow.Height - AddAccount.Height) / 2);
}
else
{
//show the added user
List<UserInfo> items = new List<UserInfo>();
items.Add(new UserInfo()
{
userInfo = "abcdefg",
imagePath = ""
});
accounts.ItemsSource = items;
AccountsTextBlock.Visibility = Visibility.Visible;
accounts.Visibility = Visibility.Visible;
}
}
}
private void OnAddAccount(object sender, RoutedEventArgs e)
{
SignInUrlWindow signInUrlWindow = new SignInUrlWindow();
signInUrlWindow.Left = Application.Current.MainWindow.Left;
signInUrlWindow.Top = Application.Current.MainWindow.Top;
signInUrlWindow.Width = Application.Current.MainWindow.Width;
signInUrlWindow.Height = Application.Current.MainWindow.Height;
signInUrlWindow.ShowDialog();
List<UserInfo> items = new List<UserInfo>();
items.Add(new UserInfo()
{
userInfo = "abcdefg",
imagePath = ""
});
accounts.ItemsSource = items;
accounts.Visibility = Visibility.Visible;
AddAccount.Visibility = Visibility.Visible;
AccountsTextBlock.Visibility = Visibility.Visible;
}

PropertyChanged value is null for child ViewModel property

I have an ObservableCollection of ViewModels in my main ViewModel. The binding seems to work fine since I can switch views. However, raising the ViewModelBase OnPropertyChanged method (which work for other stuff) in an element of the ObservableCollection result in a null PropertyChanged value in ViewModelBase.
Here's my main code snippets:
In my main ViewModel Constructor:
public EditorViewModel()
{
base.DisplayName = Strings.EditorName;
_availableEditors = new ObservableCollection<ViewModelBase>();
AvailableEditors.Add(new GBARomViewModel(646, 384));
AvailableEditors.Add(new MonsterViewModel(800, 500));
CurrentEditor = _availableEditors[0];
}
At GBA ROM loading, ViewModel and Model properties are updated:
void RequestOpenRom()
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.DefaultExt = ".gba";
dlg.Filter = "GBA ROM (.gba)|*.gba|All files (*.*)|*.*";
dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
if(CurrentEditor is GBARomViewModel)
{
(CurrentEditor as GBARomViewModel).ReadRom(dlg.FileName);
}
}
}
In my main View: Variation of TabControl (to have view switching and view states preservation).
<controls:TabControlEx ItemsSource="{Binding AvailableEditors}"
SelectedItem="{Binding CurrentEditor}"
Style="{StaticResource BlankTabControlTemplate}"
MinWidth="{Binding CurrentEditorWidth}"
MinHeight="{Binding CurrentEditorHeight}"
MaxWidth="{Binding CurrentEditorWidth}"
MaxHeight="{Binding CurrentEditorHeight}"
Width="{Binding CurrentEditorWidth}"
Height="{Binding CurrentEditorHeight}"
HorizontalAlignment="Left"
VerticalAlignment="Top">
<controls:TabControlEx.Resources>
<DataTemplate DataType="{x:Type vm:GBARomViewModel}">
<vw:GBARomView />
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MonsterViewModel}">
<vw:MonsterView />
</DataTemplate>
</controls:TabControlEx.Resources>
</controls:TabControlEx>
In GBARomViewModel (child ViewModel, element of AvailableEditors)
public String CRC32
{
get
{
return _rom.CRC32;
}
set
{
if (value.Equals(_rom.CRC32))
{
return;
}
_rom.CRC32 = value;
OnPropertyChanged("CRC32");
}
}
Property binding in child View
Now this is a UserControl so I'll put its code as well after. Other properties at startup work such as LabelWidth and the LabelValue. Giving a default value to TextBoxValue in XAML also work.
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="10, 0, 0, 10" Width="300">
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomTitle}" TextBoxValue="{Binding Title}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomGameCode}" TextBoxValue="{Binding GameCode}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomRomSize}" TextBoxValue="{Binding RomSize}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomCRC32}" TextBoxValue="{Binding CRC32}" />
<dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="200" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomMD5Checksum}" TextBoxValue="{Binding MD5Checksum}"/>
</StackPanel>
DefaultLabelBox.cs
<UserControl x:Name="uc">
<StackPanel>
<TextBlock Text="{Binding Path=LabelValue, ElementName=uc}"
Width="{Binding Path=LabelWidth, ElementName=uc}"/>
<Label Content="{Binding Path=TextBoxValue, Mode=OneWay, ElementName=uc}"
Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
</StackPanel>
</UserControl>
DefaultLabelBox.xaml.cs
public string TextBoxValue
{
get {
return (string)GetValue(TextBoxValueProperty);
}
set {
SetValue(TextBoxValueProperty, value);
}
}
public static readonly DependencyProperty TextBoxValueProperty =
DependencyProperty.Register("TextBoxValue", typeof(string), typeof(DefaultLabelBox), new PropertyMetadata(default(string)));
Control Template
<Style TargetType="dlb:DefaultLabelBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="dlb:DefaultLabelBox">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding LabelValue, RelativeSource={RelativeSource TemplatedParent}}"
MinWidth="20"
Width="{Binding LabelWidth, RelativeSource={RelativeSource TemplatedParent}}"
VerticalAlignment="Center"
FontFamily="Mangal"
Height="20"
FontSize="13"/>
<Label Content="{Binding TextBoxValue, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
BorderBrush="{StaticResource DefaultLabelBoxBorderBrush}"
BorderThickness="1"
Padding="1,1,1,1"
Background="{StaticResource DefaultLabelBoxBackgroundBrush}"
Foreground="{StaticResource DefaultLabelBoxForeground}"
MinWidth="60"
Height="20"
VerticalAlignment="Center"
FontFamily="Mangal"
Width="{Binding TextBoxWidth, RelativeSource={RelativeSource TemplatedParent}}"
FontSize="13"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I tried a few things but being new to MVVM I don't know if I have a DataContext issue of a binding one. Any help would be appreciated.
Edit: I made changes to some code to illustrate the working solution for me as well as adding the ControlTemplate I had forgot. I'm not sure if Mode=OneWay is mandatory in UserControl and ControlTemplate but it's working now so I'm leaving it as it is.
In order to make a binding like
<dlb:DefaultLabelBox ... TextBoxValue="{Binding CRC32, Mode=TwoWay}" />
work, the DefaultLabelBox needs to inherit its DataContext from its parent control (this is btw. the reason why a UserControl should never explicitly set its DataContext).
However, the "internal" bindings in the UserControl's XAML then need an explicitly specified Source or RelativeSource or ElementName.
So they should (for example) look like this:
<UserControl ... x:Name="uc">
<StackPanel>
<TextBlock
Text="{Binding Path=LabelValue, ElementName=uc}"
Width="{Binding Path=LabelWidth, ElementName=uc}"/>
<TextBox
Text="{Binding Path=TextBoxValue, Mode=TwoWay, ElementName=uc}"
Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
</StackPanel>
</UserControl>

Silverlight ZOrder doesn' work for me in Bing control

I'm trying to make my popup widget to be on top in a map but setting Canvas.ZOrder doesn't help.
Here is XAML:
<m:Map x:Name="MainMap"
Margin="0,6,3,3"
ZoomLevel="{Binding MapZoomLevel, Mode=TwoWay}"
Center="{Binding MapCenter, Mode=TwoWay}"
CopyrightVisibility="Collapsed"
CredentialsProvider="{Binding BingCredentialsProvider}"
UseInertia="True"
Mode="Road" Grid.Column="2" Grid.Row="1">
<m:MapItemsControl ItemsSource="{Binding Source={StaticResource WorkLayerData}}">
<m:MapItemsControl.ItemTemplate>
<DataTemplate>
<Canvas
m:MapLayer.Position="{Binding Location}">
<Button
Width="{Binding PushpinWidth}" Height="{Binding PushpinWidth}"
Margin="{Binding PushpinMargin}"
Style="{StaticResource LooklessButtonStyle}"
Command="{Binding DataContext.SelectedPushpinChangedCommand, ElementName=LayoutRoot}"
CommandParameter="{Binding}"
Cursor="Hand">
<Ellipse
Width="{Binding PushpinWidth}" Height="{Binding PushpinWidth}" Stroke="Black" Fill="{Binding IsGPSDataRecent, Converter={StaticResource BoolToGreenRedBrushConverter}}" StrokeThickness="1">
<ToolTipService.ToolTip>
<TextBlock Text="{Binding DeviceId}" />
</ToolTipService.ToolTip>
</Ellipse>
</Button>
<!-- Show black dot over actual GPS point -->
<Ellipse
Width="10" Height="10" Stroke="Black" Fill="Black" StrokeThickness="1"
Margin="-5,-5,0,0"
Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}" />
<Border
Width="200"
BorderThickness="1" BorderBrush="DarkGray"
Visibility="{Binding IsSelected, Converter={StaticResource BoolToVisibilityConverter}}">
<Border.Effect>
<DropShadowEffect BlurRadius="5" Color="#FF000000" Opacity="0.5" ShadowDepth="2" />
</Border.Effect>
<ContentControl Template="{StaticResource TrackedAssetControlTemplate}" />
</Border>
</Canvas>
</DataTemplate>
</m:MapItemsControl.ItemTemplate>
</m:MapItemsControl>
</m:Map>
I tried to set ZIndex on a border but no luck.
Here is how it looks when IsSelected = true (see other dots with ZIndex higher on top)
In order to bring an item in a MapItemsControl to front it is necessary to set the ZIndex of the item container. You can do that in code behind by retrieving the item container from the MapItemsControl's ItemContainerGenerator.
In case you don't want that, you could apply an attached helper property to the top-level container (the Canvas) in the DataTemplate for your map items. As this Canvas is the direct child of the item container, the helper property would have to set the ZIndex of the visual parent of the Canvas. If that sounds weird, here is the code for the attached property, in a helper class called MapItem:
public class MapItem
{
public static readonly DependencyProperty ZIndexProperty =
DependencyProperty.RegisterAttached("ZIndex", typeof(int),
typeof(MapItem), new PropertyMetadata(ZIndexChanged));
public static int GetZIndex(DependencyObject obj)
{
return (int)obj.GetValue(ZIndexProperty);
}
public static void SetZIndex(DependencyObject obj, int value)
{
obj.SetValue(ZIndexProperty, value);
}
private static void ZIndexChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
// set ZIndex on parent of obj
Canvas.SetZIndex((UIElement)VisualTreeHelper.GetParent(obj), (int)e.NewValue);
}
}
In your DataTemplate you may now bind the helper property to one of your VM properties, perhaps by using an appropriate binding converter:
<DataTemplate x:Key="MapItemDataTemplate">
<!-- setting the helper property MapItem.ZIndex on Canvas
sets the Canvas.ZIndex property on the item container -->
<Canvas local:MapItem.ZIndex="{Binding ...}">
...
</Canvas>
</DataTemplate>

WPF & Prism 4.1 Garbage Collection / Memory Issues

I built a Prism application using WPF, .Net 4, Prism 4.1, and Unity. I'm using a DirectoryModuleCatalog to find modules at runtime. My views are displayed in a TabControl (MainRegion). When I remove a view from the region, the view and viewmodel remain in memory and never get garbage collected - the tabitem is removed. After many hours searching, I cannot figure out what I'm doing wrong.
Here's my bootstrapper:
public class Bootstrapper : UnityBootstrapper
{
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
}
protected override DependencyObject CreateShell()
{
var shell = new Shell();
return shell;
}
protected override IModuleCatalog CreateModuleCatalog()
{
return new DirectoryModuleCatalog() { ModulePath = #".\Modules" };
}
}
Here's my module:
[Module(ModuleName = "ModuleA")]
public class Module : IModule
{
private IRegionManager _regionManager;
public Module(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void Initialize()
{
var view = new UserControl1();
//_regionManager.RegisterViewWithRegion("MainRegion", typeof(UserControl1));
_regionManager.Regions["MainRegion"].Add(view, "ModuleA");
_regionManager.Regions["MainRegion"].Activate(view);
}
}
And heres the viewmodel for my view that gets added to the region:
public class ViewModel
{
public DelegateCommand RemoveView { get; set; }
public ViewModel()
{
RemoveView = new DelegateCommand(() =>
{
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
var view = regionManager.Regions["MainRegion"].GetView("ModuleA");
regionManager.Regions["MainRegion"].Deactivate(view);
regionManager.Regions["MainRegion"].Remove(view);
});
}
}
And here's the code behind for the view:
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.DataContext = new ViewModel();
}
}
I've read that it could be because I'm instantiating the view in the module or perhaps the viewmodel in the view? When I use Red Gate Memory Profiler, and remove the view via the DelegateCommand, the view and viewmodel are both flagged as not being able to be garbage collected. Where is the reference that I'm not properly cutting?
Heres the retention graph from Ants: https://docs.google.com/file/d/0B4XjO9pUQxBXbGFHS1luNUtyOTg/edit?usp=sharing
Here's a test solution showing the issue.
Also, I posted the question on CodePlex as well.
It looks like you have a binding reference to it still in your retention graph.
Read and understand the following:
A memory leak may occur when you use data binding in Windows Presentation Foundation
I think it may be your problem, but you didn't show your actual bindings.
Finally found the root cause of my problem....
In our Shell.xaml we were binding IsDefault in one of our buttons to a PasswordBox's IsKeyboardFocused:
<Button Style="{DynamicResource RedSubmitButtonStyle}" IsDefault="{Binding ElementName=passwordBox1, Path=IsKeyboardFocused}" Command="{Binding LoginCommand}" Content="Login" Height="23" HorizontalAlignment="Left" Margin="145,86,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
IsKeyboardFocused, is a dependency property according to MSDN - so should be good on that end.
It was related to the attached property we had on the Password box that allows us to bind to the Password entered. The focus was remaining on that password box even after we hid the ChildWindow (from WPF toolkit extended). So instead of using IsDefault, I added a keydown event to the PasswordBox and if it was Key.Enter, I would change the focused UI control and log the person into the program.
Here's the full contents of our Shell.xaml file
<Grid x:Name="MainGrid" core:SharedResourceDictionary.MergedDictionaries="TabControlThemes;MenuThemes;ButtonThemes;DataGridThemes;TreeViewThemes;ComboBoxThemes;ListBoxThemes;GroupBoxThemes;ToggleSwitchThemes">
<DockPanel>
<ContentControl x:Name="menuContent" DockPanel.Dock="Top" prism:RegionManager.RegionName="MenuRegion" />
<ContentControl DockPanel.Dock="Bottom" prism:RegionManager.RegionName="FooterRegion" />
<ContentControl DockPanel.Dock="Top" prism:RegionManager.RegionName="MainContentRegion" />
</DockPanel>
<StackPanel Orientation="Horizontal" Panel.ZIndex="4" HorizontalAlignment="Right" VerticalAlignment="Top">
<Button Visibility="{Binding IsFullScreenToggleVisible, Converter={StaticResource visConv}}" Command="{Binding ToggleFullScreen}" Height="50" Name="button4" Width="70" HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top">
<Button.Content>
<TextBlock FontSize="12" FontWeight="Bold" TextWrapping="Wrap" TextAlignment="Center" HorizontalAlignment="Center" VerticalAlignment="Center" Text=" Toggle Full Screen" />
</Button.Content>
</Button>
<Button Visibility="{Binding IsAppCloseButtonVisible, Converter={StaticResource visConv}}" Command="{Binding ShutdownApplication}" Height="50" Name="button3" Width="50" HorizontalAlignment="Right" Margin="0,10,10,0" VerticalAlignment="Top">
<Button.Content>
<Image Source="..\Graphics\close.png" Name="image1" />
</Button.Content>
</Button>
</StackPanel>
<xctk:ChildWindow Name="loginChildWindow" Panel.ZIndex="3" CloseButtonVisibility="Collapsed" FocusedElement="{Binding ElementName=usernameTextBox}" WindowStartupLocation="Center" WindowState="{Binding IsVisible, Mode=TwoWay, Converter={StaticResource boolConverter}}" IsModal="True" OverlayOpacity="1" Caption="Pioneer Login" Height="164" Width="261">
<xctk:ChildWindow.OverlayBrush>
<ImageBrush Stretch="None" Viewport="0,0,46,29" ViewportUnits="Absolute" ImageSource="../Graphics/escheresque.png" TileMode="Tile" />
</xctk:ChildWindow.OverlayBrush>
<xctk:BusyIndicator IsBusy="{Binding IsLoginBusy}" BusyContent="Authenticating...">
<Grid>
<TextBox GotFocus="usernameTextBox_GotFocus" Text="{Binding Username, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="99,20,0,0" Name="usernameTextBox" VerticalAlignment="Top" Width="120">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding LoginCommand}" />
</TextBox.InputBindings>
</TextBox>
<Label Content="Username" Height="28" HorizontalAlignment="Left" Margin="28,18,0,0" Name="label1" VerticalAlignment="Top" />
<Label Content="Password" Height="28" HorizontalAlignment="Left" Margin="29,47,0,0" Name="label2" VerticalAlignment="Top" />
<PasswordBox attach:PasswordBoxAssistant.BindPassword="True" attach:PasswordBoxAssistant.BoundPassword="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Height="23" HorizontalAlignment="Left" Margin="100,50,0,0" Name="passwordBox1" VerticalAlignment="Top" Width="120" />
<Button Style="{DynamicResource RedSubmitButtonStyle}" IsDefault="{Binding ElementName=passwordBox1, Path=IsKeyboardFocused}" Command="{Binding LoginCommand}" Content="Login" Height="23" HorizontalAlignment="Left" Margin="145,86,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
<Button Style="{DynamicResource RedSubmitButtonStyle}" Command="{Binding LazyLoginCommand}" Visibility="{Binding IsDebugMode, Converter={StaticResource visConv}}" Content="Quick Login" Height="23" HorizontalAlignment="Left" Margin="23,87,0,0" Name="button2" VerticalAlignment="Top" Width="89" />
</Grid>
</xctk:BusyIndicator>
</xctk:ChildWindow>
</Grid>

Custom button with property as StaticResource

I am trying to achieve the following thing: use an svg image into a custom button.
In order to do this I created a Custom button:
public class MainButton : Button
{
static MainButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MainButton),
new FrameworkPropertyMetadata(typeof(MainButton)));
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MainButton),
new UIPropertyMetadata(""));
public object Image
{
get { return (object)GetValue(ImageProperty); }
set { SetValue(ImageProperty, value); }
}
public static readonly DependencyProperty ImageProperty =
DependencyProperty.Register("Image", typeof(object), typeof(MainButton),
new UIPropertyMetadata(""));
}
I took a svg file, opened it in inkscape and saved it as xaml file.
I opened Themes.xaml and added the created xaml image as a ControlTemplate
<ControlTemplate x:Key="Home">
<Viewbox Stretch="Uniform">
<Canvas .....
</Canvas>
</Viewbox>
</ControlTemplate>
And the button style is:
Style TargetType="{x:Type local:MainButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MainButton}">
<Canvas x:Name="sp" Width="80"
Height="80">
<StackPanel Canvas.Top="12"
Canvas.Left="0"
Canvas.ZIndex="2"
Width="80">
<ContentControl x:Name="Img" Template="{StaticResource Home}" />
</StackPanel>
<StackPanel x:Name="spText" Canvas.Top="45"
Canvas.Left="1"
Canvas.ZIndex="1"
Width="80">
<TextBlock x:Name="Txt" Text="{Binding Path=(local:MainButton.Text),
RelativeSource ={RelativeSource FindAncestor,
AncestorType ={x:Type Button}}}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Foreground="White"
FontSize="14"/>
</StackPanel>
...
As you can see I have hardcoded the StaticResource name <ContentControl x:Name="Img" Template="{StaticResource Home}" />
I want to be able to have a binding with property Image on this Template, something like
<ContentControl x:Name="Img" Template="{StaticResource {???Binding Image???}}" />
So that I can set the Image property of the button with the name of the StaticResource I want.
For example, having beside "Home" image, another one "Back" I would have two buttons in MainWindow declared like this:
<MyControls:MainButton Text="Go Home!" Image="Home" />
<MyControls:MainButton Text="Go Back!" Image="Back" />
Any advice is kindly taken. Thank you for your time.
You can achieve the same result without creating a custom button. For example, in the following code, I created an image into the grid resource and then I use it inside the button's content.
<Grid>
<Grid.Resources>
<DrawingImage x:Key="Home">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Brush="#FFFFFFFF" Geometry="F1 M 0,20L 30,20L 30,40L 0,40 Z ">
<GeometryDrawing.Pen>
<Pen Thickness="2" LineJoin="Round" Brush="#FF000000"/>
</GeometryDrawing.Pen>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
</Grid.Resources>
<Button>
<StackPanel Orientation="Horizontal">
<Image Source="{StaticResource ResourceKey=Home}" />
<TextBlock Text="Hello!" />
</StackPanel>
</Button>
</Grid>
To add more images, you can simply add another DrawingImage into the grid resource and then use it in the same way into another button.

Resources