Simple WPF Button Control Template with variable images? - c#

I am still new to WPF (writing my first app with it), but basically I am wanting (or think I want) to create a control of somekind that has 3 images (idle, hover, onClick) but that I can change the images. So now I have:
<Rectangle Height="29" Width="35" Margin="0,2,0,0"
HorizontalAlignment="Left" VerticalAlignment="Top"
Name="BTN_OpenDeviceSettings" ToolTip="Open Device Settings"
Fill="{DynamicResource device_grid___open_grid}"
MouseEnter="BTN_OpenDeviceSettings_MouseEnter"
MouseLeave="BTN_OpenDeviceSettings_MouseLeave"
MouseLeftButtonDown="BTN_OpenDeviceSettings_MouseLeftButtonDown"
MouseLeftButtonUp="BTN_OpenDeviceSettings_MouseLeftButtonUp">
</Rectangle>
And it works great. But I want to separate the graphics from the code, and to make this work I manually swap the fill image in my C# code. I have seen code like:
<Button Name="btnNext" Padding="15,3" HorizontalAlignment="Right" Click="OnButtonClick">
<Image Width="90" Height="90">
<Image.Style>
<Style TargetType="Image">
<Setter Property="Source" Value="/resources/a.png" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Source" Value="/resources/b.png" />
</Trigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Button>
So I think that is what I am wanting. But ideally, I am going to have many of these buttons, so can I create something so that I do this:
<MyButton>
<Images idleImage="someimage.png" hoverImage="someOtherImage.png" clickImage="someOtherOtherImage.png" onCLick="some_cSharp_method_to_call" />
</MyButton>

First solution: you can create a control template(or style if you are planning to show only images within a button) for buttons which will declare what images will be used for overlapping/clicking/other operations. This style will be shared by all buttons you want because it will have a designated key.
Second solution: you can subclass button and declare 3 dependency properties for hover/idle/clicked images. Then again you will need some default template(or style if button only displays images) for your subclassed button which will use that dependency properties.

Related

C# WPF - Change control content when another control event occurs

I'm quite new to WPF and MVVM and I'm trying to create a custom WindowChrome with all the standard Window features. I'm struggling with the Maximize/Minimize window Button content: I want the content to change when the user double clicks the WindowChrome bar, in order to show the right icon:
When I double click the bar, the result should be:
I managed to change the content with the Button Triggers, but how can I change it when another control event occurs?
Thanks in advance for the help!
Give the Button a Style with triggers that set the content based on the value of Window.WindowState. This isn't an event. The button reflects the current state of the window.
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger
Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType=Window}}"
Value="Maximized">
<Setter Property="Content">
<Setter.Value>
<!-- I don't know if you're using a Path or what -->
<Path Stroke="White" Data="..." />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger
Binding="{Binding WindowState, RelativeSource={RelativeSource AncestorType=Window}}"
Value="Normal">
<Setter Property="Content">
<Setter.Value>
<!-- I don't know if you're using a Path or what -->
<Path Stroke="White" Data="..." />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
It would be wiser to set the Template of the button instead, because there’s only one copy of each of those Paths, and if you reuse the style twice, they can’t be shared.
If there's some reason why this won't work with your code, show me your code.

DockPanel changes color background when clicked

I set a background color for a DockPanel, but it changes when it is accidentally clicked. How to detach this behaviour from the dockpanel?
There is no IsPressed properties or similar, so I can't use this:
<Style TargetType="{x:Type DockPanel}">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
</Style.Triggers>
</Style>
This is a DockPanel used in the main window:
<DockPanel>
<TextBlock Style="{StaticResource TextBlockStyle}"
Text="Margins : "/>
<Image Source="/View/Bitmaps/Horizontal.bmp"/>
<TextBox Style="{StaticResource EditBarStyle}"
Text="{Binding ReportHorizontalMargin}"/>
<Image Source="/View/Bitmaps/Vertical.bmp"/>
<TextBox Style="{StaticResource EditBarStyle}"
Text="{Binding ReportVerticalMargin}"/>
<Button HorizontalAlignment="Right"
Command="{Binding UpdateReportMargins}">
<TextBlock Style="{StaticResource TextBlockStyle}"
Text="Modify"/>
</Button>
</DockPanel>
To answer your specific question
How to detach this behaviour from the dockpanel?
There is an important feature that is handy to know in WPF that a Background of NULL of a control (i.e.<Setter Property="Background" Value="{x:Null}"/>) is different to a background that is transparent (i.e. <Setter Property="Background" Value="Transparent"/>) with regard to hit-testing, even though visually they look the same.
Hit-testing and thus firing mouse events happens if the background is Transparent but not if the background is {x:Null}, so setting your background to {x:Null} will have the effect you are asking for.
Why is the background changing colour?
The standard DockPanel does not have this feature and I cannot deduce where this could be happening with the code snippets you have here.
Could this be the TextBox control showing blue when all the text is selected?
Have you tried viewing the application running via Snoop?
Once you point Snoop at your application running then you can select the rectangle region that is changing colour (move the mouse over you application holding down CTRL and Shift) or select the control on the Snoop panel Tree View. Snoop will then Adorn the control with a Red-ish border.
Identifying the control specifically responsible for that region of the screen might help you narrow down where the problem is.

MouseLeftButtonDown event isn't fired in child ContentControl

I have PortItem which derived from ContentControl, TextedStackPanel derived from StackPanel which contains PortItems. And in MainWindow i have 2 StackPanels which contains TextedStackPanels . In PortItem i have overridden MouseLeftButtonDown method. But when i do this on this method isn't fired. I searched here in the forum, and found that Background property of Grid/StackPanel must be set to Transparent. I applied this, but there is no changes. What to do ?
EDIT 1
I use partial classes. I have 2 classes: PortItem.cs and PortItem.cs.xaml. I modifiy any visual changes in this XAML file.
EDIT 2
Also any mouse events aren't fired. Triggers which i use IsMouseOver are also dont work when i keep mouse on PortItem
XAML
<ContentControl x:Class="**.PortItem"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UI="clr-namespace:**.UIData" Width="17" Height="17" Margin="3" SnapsToDevicePixels="True" >
<Grid Background="Transparent" Name="mainGrid">
<!-- transparent extra space makes connector easier to hit -->
<Rectangle Fill="Transparent" Margin="-2"/>
<Border BorderBrush="Green" x:Name="border" BorderThickness="2">
<Border.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Grid}, Path=IsMouseOver}" Value="True">
<Setter Property="Border.BorderBrush" Value="Blue"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter Property="Border.BorderBrush" Value="Blue"/>
</DataTrigger>
<!--<DataTrigger Binding="{Binding ContactPort}" Value="{x:Null}">
<Setter TargetName="border" Property="Border.BorderBrush" Value="Green"/>
</DataTrigger>-->
</Style.Triggers>
</Style>
</Border.Style>
<Image Source="/**;component/Resources/1337238611_port.png">
</Image>
</Border>
</Grid>
Make sure that you haven't set 'IsHitTestVisible' to false on your PortItem. Also, make sure no other controls are on top of it. If they are, set their 'IsHitTestVisible' property to false and then your PortItem control will get the mouse right click event. To make sure nothing is on top, declare your put your PortItem as the last thing added to your TextedStackPanel. To double check that nothing else is on top, change the background color of other controls to something really noticeable (just for testing) to see if anything is covering your PortItem control. Also, change the color on your PortItem control to verify that it is really where you think it is. Then once you get it all working, change the colors back to their original colors.
If you could give us a code sample of your XAML, that might help. If you're adding the PortItems dynamically in code behind, supply that code too.
Edit: in light of the changes you've made to your code, try to add ClipToBounds="False" to the top of your user control declaration.
<ContentControl x:Class="**.PortItem" ClipToBounds="False"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:UI="clr-namespace:**.UIData" Width="17" Height="17" Margin="3" SnapsToDevicePixels="True" >
Have you created a template for your PortItem? I created the follow class to replicate your PortItem and break point on the base.OnMouseButtonDown line and it fires, I think the reason your method is not executing is because there is no visual element for the mouse to actually interact with, try adding the style below to your app and you should see the method fire properly.
public class PortItem: ContentControl
{
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
}
}
then in XAML I created a style to give it something to render.
<local:PortItem Margin="44,36,156,95">
<local:PortItem.Style>
<Style TargetType="{x:Type local:PortItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:PortItem}">
<Border Background="Transparent">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</local:PortItem.Style>
</local:PortItem>
The background being Transparent that you mention you can see in the border control, if you leave the background out you are correct, the event never fires.

WPF displaying differently on different computers

I am running Windows XP on my computer, and another computer also is running Windows XP.
In my WPF application, I've changed the styles of my buttons to appear as if they are not highlighting when rolled over, clicked, etc.
On my computer, this is the behavior that is occurring. On the other Windows XP system, I am seeing some outlining of the buttons with I roll over them or click.
Any ideas on why this is happening?
EDIT
Here is the Button itself
<Button Click="Next_Click" Width="100" VerticalAlignment="Center" BorderThickness="0" BorderBrush="Black" Background="Black" IsTabStop="False" DockPanel.Dock="Right" HorizontalAlignment="Left" Height="154" Name="NextOffers">
<Image Source="Images/offer_right_arrow.jpg" Width="100" Height="154" MaxWidth="100" MaxHeight="154" HorizontalAlignment="Left" VerticalAlignment="Top" MinWidth="100" MinHeight="154" ></Image>
</Button>
Also this Style, too.
<Style TargetType="{x:Type ListBoxItem}" x:Key="ListBoxItemStyle">
<EventSetter Event="PreviewMouseLeftButtonDown" Handler="ListBox_MouseLeftButtonDown"></EventSetter>
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Name="Border" Padding="0,0,2,0" SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="true">
<Setter TargetName="Border" Property="Background" Value="Black"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I don't have a template made for this button, obviously.
I am loading my ResourceDictionaries dynamically in the code behind.
It sounds like you need a custom control template for your buttons. The default control template for most WPF controls is dependent upon the Windows theme that is selected, so that a button follows the theme whether you're on the Windows Classic theme, Aero, Royale, or whatever. If you want it to look exactly the same no matter what OS or theme the user has chosen, you'll save yourself a lot of headache using a custom control template.
Google "Show me the template" for an app that will give you the control template source (XAML) for each theme. This is a good starting point for creating custom templates.
The problem you're seeing is most likely due to the default Button Chrome. You can apply all sorts of styling, but if you leave that Chrome intact, the Windows theme is going to rear its ugly head.
You'll need to recreate the ControlTemplate itself.

How can I set a border around a control during runtime in WPF?

I have an Image control on my WPF Form. How can I create a border around it during runtime?
Here's my XAML code:
<Image Margin="2.5"
Grid.Column="1" Grid.Row="0"
x:Name="Behemoth" Source="Images/Hero/Behemoth.gif" Stretch="Fill"
MouseEnter="HeroMouseEnter"
MouseLeave="HeroMouseLeave"
MouseDown="HeroMouseClick" />
Also, I want to know how to remove the border.
Maybe if I state my problem better there is an even better solution available.
I have many Images, and when a user says: "Hey, just show me the woman out of all the picture." I want a way to sort of highlight or draw the users attention to whatever images I need them to see. I was thinking about adding a border, but maybe that's too much work for something that can be solved easier.
Any help?
Although it's visually very different from a border, you could use an outter glow to signify the importance of the image. Then, you don't have to change the parent of the image.
Alternatively, you could use a custom Adorner to place a border around the image. Good info on Adorners can be found on msdn.
There's no straightforward way to do it, because the Border is a container, so you would have to remove the Image from its parent, put the Border instead, and put the Image back in the Border...
Another option would be to use templates :
<Window.Resources>
<ControlTemplate x:Key="imageWithBorder" TargetType="{x:Type Image}">
<Border BorderBrush="Red" BorderThickness="2">
<Image Source="{TemplateBinding Source}" />
</Border>
</ControlTemplate>
</Window.Resources>
...
<Image Name="image1" Source="foo.png"/>
When you want to put the border around the image, just assign the template to the image :
image1.Template = this.FindResource("imageWithBorder") as ControlTemplate;
For your stated needs, I suggest you use a ListBox with a custom ItemContainerStyle - one that always has a border but only makes it visible if the item is selected.
Here's the basic idea:
<ListBox ItemsSource="{Binding MyImageObjects}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<Border x:Name="border">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ListBoxItem.IsSelected" Value="True">
<Setter ElementName="border" Property="BorderBrush" Value="Blue" />
<Setter ElementName="border" Property="BorderThickness" Value="2" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Categories

Resources