Hi all. I create an application with some (WPF) forms. I have a button on one form. I want to, at run time, copy this button onto a second form without having to create it again.
For example: I have 'X' button on form 1. At run time, I create a copy of this button on form 2 with all of the same property values as the original button on form 1.
The button:
<Button Content="Open Window" Click="ButtonClicked" Height="25"
HorizontalAlignment="Left" Margin="379,264,0,0" Name="button1"
VerticalAlignment="Top" Width="100" />
Any chance I can avoid having to reproduce this code?
You can define your own style for button in App.xaml:
<Application x:Class="WpfButton.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style x:Key="myBtnStyle" TargetType="Button" >
<Setter Property="Content" Value="Open Window" />
<Setter Property="Height" Value="25" />
<Setter Property="Width" Value="100" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Margin" Value="379,264,0,0" />
<Setter Property="VerticalAlignment" Value="Top" />
<EventSetter Event="Click" Handler="myBtn_Click" />
</Style>
</Application.Resources>
</Application>
Code behind:
public partial class App : Application
{
public void myBtn_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
// ...
}
}
And when you want to assign created earlier style to the button, you should use StaticResource and name of your style:
<Button Style="{StaticResource myBtnStyle}" />
If you want to create an exact clone, then you will need to serialize the component and inject an ExpressionConverter into the serialization process. This will create a 'deep' clone. See here.
Related
how can i define a global button and use it in multiple places in WPF.
here is my button witch i want to use it in multiple places.
<Button x:Key="Attach" Width="90" Margin="220,0,0,0" Content="Attach" Height="16" FontSize="11"/>
however i tried to define it in App.xaml(Application.Resources)
and also in MainWindow.xaml (inside Window.Resources)
But i cannot access it in CodeBehind
Button button = Resources["Attach"];
My question is where to define my button and if i defined it correct how to use it in CodeBehind and XAML.
In your App.xaml you will have to add and define a style that you want for your buttons.
<Application.Resources>
<Style x:Key="Attach" TargetType="{x:Type Button}">
<Setter Property="Height" Value="16" />
<Setter Property="Width" Value="90" />
<Setter Property="Content" Value="Attach" />
<Setter Property="Margin" Value="220,0,0,0" />
<Setter Property="FontSize" Value="11" />
</Style>
</Application.Resources>
And to access it in your code-behind you will need to initialize a new style object and populate it with the style you created in your App.xaml. Lastly just add that new style to the style property of your button.
Style style = this.FindResource("Attach") as Style;
Button.Style = style;
In your MainWindow.xaml
<Window.Resources>
<HierarchicalDataTemplate
x:Key="TreeViewMainTemplate"
ItemsSource="{Binding SubTopics}">
<Button
Width="90"
Margin="220,0,0,0"
Content="Attach"
Height="16"
FontSize="11" />
</HierarchicalDataTemplate>
</Window.Resources>
Defining a HiercharchicalDataTemplate with your button layout will allow you to re-use it as an ItemTemplate in your TreeView:
<TreeView
Name="TopicTreeView"
ItemsSource="{Binding Topics}"
ItemTemplate="{StaticResource TreeViewMainTemplate}">
</TreeView>
As you see I'm making intensive use of binding for resources as well as data because I'm building my wpf/sl apps the MVVM way. Doing so makes the need to access controls from code behind obsolete and might be worth looking into for you.
I've written a WPF plugin for some off-the-shelf product, and in this plugin I've used a theme/style to change the minimal width of all buttons like so:
<Style TargetType="Button">
<Setter Property="MinWidth" Value="80" />
</Style>
In the newest version of said off-the-shelf product they migrated from winforms to WPF themselves. Now when my plugin is loaded the style that previously just affected my plugged-in forms now affects all buttons in the application. This renders most UI's unusable.
I know I can use dictionary key based resources to make this style specific to my buttons, but this means I have to change each and every button in my plugin by hand, as well as not forget to set the style of each button in the future (and other elements this problem applies to). There are other options to make the style specific to a set of buttons, as seen in Is it possible to set a style in XAML that selectively affects controls? But I'm looking for a way to let my style affect only those of my plugin (so a bit more coarse than talked about in the referenced question). This plugin consists of multiple windows/views (tied together with Caliburn.Micro).
Is there a way to easily scope a style to for instance an assembly or namespace? I'd really like to define my resources just once. Currently it's defined at the Application.Resources level, if there's one more appropriate I'd like to hear that too.
With a ResourceDictionary, we can set default style wich will be applied without define style in Xaml.
DictionayNew.xaml :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Media="clr-namespace:System.Windows.Media;assembly=PresentationCore"
xmlns:System="clr-namespace:System;assembly=mscorlib">
<!-- default button -->
<Style TargetType="{x:Type Button}">
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="MinWidth" Value="80" />
</Style>
<!-- new button style -->
<Style x:Key="ActionButton" TargetType="{x:Type Button}">
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="MinWidth" Value="75" />
<Setter Property="Height" Value="23" />
</Style>
<!-- new button style based on previous style -->
<Style x:Key="BigActionButton"
BasedOn="{StaticResource ActionButton}"
TargetType="{x:Type Button}">
<Setter Property="MinWidth" Value="150" />
<Setter Property="Height" Value="30" />
</Style>
</ResourceDictionary>
In your Xaml, use the dictionary :
<Window x:Class="CheckDoublonImageBing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DictionaryNew.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
</Grid>
</Window>
Then, use Button as usual or with new style
<Button Content="Refresh" />
<Button Content="Delete selected" Style="{DynamicResource ActionButton}" />
With no style defined, button will have default style defined in the dictionary.
EDIT :
You can set merged dictionary by code like this :
ResourceDictionary myResourceDictionary = new ResourceDictionary();
myResourceDictionary.Source = new Uri("DictionayNew.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Add(myResourceDictionary);
You need to specify a Key for your Style and apply the Style to all your Buttons.
<Style TargetType="Button" x:Key="MyButtonStyle">
<Setter Property="MinWidth" Value="80" />
</Style>
<Button Style="{StaticResource MyButtonStyle}"/>
Without a Key the Style is used for all Buttons in the application (as you have already noticed).
I have a (NoResize Borderless) window in WPF that I am trying to have sizable yet still have the system menu work.
I have manually done NC hittesting to enable resizing and the system menu. However, I can only get one to work at a time. I try to enable both using SetWindowLong(Ptr) but I cannot get any combination to enable the system menu and resizing.
I've tried enabling the WS_SYSMENU style which makes no difference.
PostMessage(callingWindow, WindowMessage.SystemCommand, new IntPtr(trackPMenu), IntPtr.Zero);
only works when the window is not resizable.
Also, setting the window to be an overlapped window (WS_OVERLAPPEDWINDOW) puts a frame around the window.
The MahApps OSS project has the ability to have a no resizable borderless window with a system menu. Have a look at there MetroWindow class.
I'm currently using a resizable borderless window with a system menu:
Code behind:
using MahApps.Metro.Controls;
public partial class MainWindow : MetroWindow
{
public MainWindow()
{
InitializeComponent();
}
}
XAML:
<controls:MetroWindow x:Class="XXXX.Views.MainWindow"
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"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:vm="clr-namespace:XXXX.ViewModels"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
d:DataContext="{d:DesignInstance vm:MainViewModel}"
WindowStartupLocation="Manual"
SaveWindowPosition="True"
Title="{Binding Path=Title, Mode=OneWay}"
Style="{StaticResource MyCleanWindowStyleKey}">
</controls:MetroWindow>
With the following style:
<Style x:Key="MyCleanWindowStyleKey"
TargetType="{x:Type c:MetroWindow}">
<Setter Property="BorderThickness"
Value="2" />
<Setter Property="BorderBrush"
Value="Blue" />
<Setter Property="Background"
Value="Black" />
<Setter Property="Foreground"
Value="White" />
<Setter Property="TitleForeground"
Value="White" />
<Setter Property="TitlebarHeight"
Value="42" />
<Setter Property="WindowState"
Value="Normal" />
<Setter Property="ResizeMode"
Value="CanResizeWithGrip" />
<Setter Property="AllowsTransparency"
Value="False" />
<Setter Property="TitleCaps"
Value="False" />
<Setter Property="ShowWindowCommandsOnTop"
Value="False" />
<Setter Property="WindowTransitionsEnabled"
Value="False" />
</Style>
I have a WPF button like so:
<Page.Resources>
<ImageBrush x:Key="black_pane_normal" ImageSource="/Images/TroubleShooting/black_pane_clear.png" />
</Page.Resources>
<Button x:Name="ButtonBlackPane" Background="{StaticResource black_pane_normal}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="157" Height="136" MouseEnter="ButtonBlackPane_MouseEnter" MouseLeave="ButtonBlackPane_MouseLeave" Click="ButtonBlackPane_Click" RenderTransformOrigin="0.533,0.281" Margin="189,199,0,0"/>
My C# code behind is:
BitmapSource _black_pane_yellow_border = Imaging.CreateBitmapSourceFromHBitmap(InstallerToolkit.Properties.Resources.black_pane_yellow.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
BitmapSource _black_pane_no_border = Imaging.CreateBitmapSourceFromHBitmap(InstallerToolkit.Properties.Resources.black_pane_clear.GetHbitmap(),
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
private void ButtonBlackPane_MouseEnter(object sender, MouseEventArgs e)
{
ButtonBlackPane.Background = new ImageBrush(_black_pane_yellow_border);
}
private void ButtonBlackPane_MouseLeave(object sender, MouseEventArgs e)
{
ButtonBlackPane.Background = new ImageBrush(_black_pane_no_border);
}
My first problem is that my image does not fill the whole button background, how do I get this to fill it?
My second problem is that when the mouse enters the button, the correct background image gets displayed for a moment and then the default gray button image shows and my image goes away, how can I solve this?
First, it's not a good practice to set first the background with a static resource and then use code to set it each time to a new ImageBrush object. Have a look at the FindResource method (http://msdn.microsoft.com/de-de/library/system.windows.frameworkelement.findresource(v=vs.110).aspx) to use your resources in code behind.
Second, you should use Styles and Triggers to modify the appearance of a control depending for example on the MouseOver-State. There is a property called IsMouseOver which you can use for that. Here is an example:
<Page.Resources>
<Style TargetType="Button" x:Key="myButtonStyle">
<Setter Property="Foreground" Value="Red" />
<Setter Property="FontSize" Value="12" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Foreground" Value="Blue" />
<Setter Property="Cursor" Value="Hand"/>
<Setter Property="FontSize" Value="16" />
</Trigger>
</Style.Triggers>
</Style>
</Page.Resources>
<Button Width="200" Height="100" Style="{StaticResource myButtonStyle}">Hello, World!</Button>
Third, because of the default template of a WPF button, you cannot change directly the background of it when hovering with the mouse. This problem is discussed and solved here: How do you change Background for a Button MouseOver in WPF?
If you want to have more Information about styling WPF controls and how to use the default templates, have a look here: http://msdn.microsoft.com/en-us/library/aa970773.aspx
I am using the DevComponents TabNavigation control for WPF, and am able to add a new TabItem to the TabNavigation at a specific index, call it i, in the code-behind. Now I want to make the new TabItem the SelectedItem, by doing:
private void textBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
int i = createNewTabItem(0, "Foo");
TabNavigation tn = (((sender as TextBlock).Parent as Grid).Parent as TabItem).Parent as TabNavigation;
tn.SelectedItem = tn.Items[i];
}
private int createNewTabItem(int overflowSrcPageNum, String header)
{
TabItem ti = new TabItem();
ti.Header = header;
tabNavigation.Items.Insert(overflowSrcPageNum + 1, ti);
return overflowSrcPageNum + 1;
}
When I run this code, however, instead of the new TabItem being brought into view, it is brought into view and then the original tab I was on is quickly moved back into view.
If anyone has any ideas as to why this is happening, and how I can fix it please let me know. I have attached a sample of the XAML below:
<Grid >
<Grid.Resources>
<ResourceDictionary>
<Style TargetType="TextBlock">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextDecorations" Value="Underline"></Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Foreground" Value="White" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="11" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="HorizontalAlignment" Value="Right" />
<Setter Property="Text" Value="View More..." />
<Setter Property="Visibility" Value="Visible" />
<EventSetter Event="MouseLeftButtonDown" Handler="lblMoreCpartys_MouseLeftButtonDown" />
</Style>
</ResourceDictionary>
</Grid.Resources>
<my:TabNavigation Background="Black" HorizontalAlignment="Stretch" Margin="0" Name="tabNavigation"
VerticalAlignment="Stretch" MouseLeftButtonDown="tabNavigation_MouseLeftButtonDown"
FontSize="12" Foreground="SteelBlue" ForceCursor="True" MouseWheel="tabNavigation_MouseWheel"
TabStripPlacement="Bottom">
<TabItem Header="ITEM 1" Name="firstTabItem" FontSize="12" >
<TextBlock Name="firstTB" />
</TabItem>
<TabItem Header="ITEM 2" Name="secondTabItem" FontSize="12" >
<TextBlock Name="secondTB" />
</TabItem>
</my:TabNavigation>
</grid>
Thanks in advance.
Try setting e.Handled to True in textBlock_MouseLeftButtonDown.
I'm not familiar with that control, but if it works like TabControl then it has logic to bring a tab into view when it is clicked. That logic sees that the original tab was clicked, and brings it back into view after your change. Marking the EventArgs object as Handled will stop WPF from calling event handlers on parent elements, which would stop the tab from switching back.