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.
Related
So, I've got a custom WPF control called WatermarkTextbox which extends TextBox. The only thing I added to the code is a string dependency property to hold the watermark text. The rest of the magic is in the Xaml (below).
<Style TargetType="wpfControls:WatermarkTextbox" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type wpfControls:WatermarkTextbox}">
<Grid>
<TextBox x:Name="baseTextBox" />
<TextBlock Margin="5,0,0,0" x:Name="watermarkText" IsHitTestVisible="False" FontWeight="Light" FontStyle="Italic" Foreground="DarkGray" Visibility="Hidden" Background="Transparent"
Text="{Binding RelativeSource={RelativeSource AncestorType=wpfControls:WatermarkTextbox}, Path=Watermark}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="baseTextBox" Property="Text" Value="">
<Setter TargetName="watermarkText" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and when used in my application:
<wpfControls:WatermarkTextbox Watermark="Text that disappears."/>
This works, mostly. I can set the watermark text, and when I start entering some text, it goes away. When I change the font size, it changes both the watermark and the text I enter; when I change the font weight, it only changes the text entered (which is what I want it to do). I can change the size of the textbox. That's all gravy.
The problem is when I start trying to change things like the textbox's background or border properties, like so.
<wpfControls:WatermarkTextbox Watermark="Text that disappears." Background="Yellow"/>
Nothing happens. Same behavior with BorderBrush and BorderThickness. Now, the part where I know just enough to know that there's some important concept that I don't know. If I change the template for my WatermarkTextbox to the following, it will let me set the background in my application like I want.
<Style TargetType="wpfControls:WatermarkTextbox" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type wpfControls:WatermarkTextbox}">
<Grid>
<TextBox x:Name="baseTextBox"
Background="{TemplateBinding Background}"/>
<TextBlock Margin="5,0,0,0" x:Name="watermarkText" IsHitTestVisible="False" FontWeight="Light" FontStyle="Italic" Foreground="DarkGray"
Text="{Binding RelativeSource={RelativeSource AncestorType=wpfControls:WatermarkTextbox}, Path=Watermark}" Visibility="Hidden" Background="Transparent"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger SourceName="baseTextBox" Property="Text" Value="">
<Setter TargetName="watermarkText" Property="Visibility" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I assume that if I did the same for BorderBrush and BorderThickness, they would work as well.
So, my question is, why? What is it about these properties that makes them behave differently from FontSize and FontWeight or Height and Width? Why do I have to explicitly set the Background to {TemplateBinding Background} but not FontSize? Also, what other properties do I need to set to the TemplateBinding in order to make them work properly?
Some of the properties are inherited from their parent automatically. explanation
This is why you don't have to set FontSize.
As for "what else", it all depends on what you want to be able to set directly on the user control.
Although this isn't bullet proof, but my general rule of thumb is "if it is a property in the Brush Tab of the property window or is purely for visual aesthetics, it probably is not inherited"
Another way to look at it - if the setting would give weird results in general, it also is probably no inherited. Example: if you set the Margin property on a Grid, imagine if every sub-element inherited the same margins.
So i typically add the template bindings for all the non-Layout, visual properties (Background, Foreground, BorderBrush, etc.). Or i just add templatebindings for any properties i want to set directly to my usercontrol. There is no need to add a template binding if you never intend to set the property (explicitly or by style).
The font properties are inherited attached properties; they're an explicit special case. Incidentally, TextElement.Foreground is one of those.
Height and Width aren't inherited, but you've got XAML there that'll size to its parent by default, and you haven't done anything to disable that behavior.
Anything else, you need to explicitly do the TemplateBinding thing -- or if they'll change at runtime, you'll need to do a relativesource binding:
{Binding PropertyName, RelativeSource={RelativeSource TemplatedParent}}
There isn't any "first principles" explanation for this; it's all just arbitrary implementation details.
I am running into a problem with WPF UI wherein I need to set the border of a GroupBox control based on the fact if there is any checkbox inside the GroupBox that has been unchecked. I have tried writing some code but that didn't work, I am new to WPF and I tried using a style to solve the problem but that didn't work well with me. The code looks as follows -:
<GroupBox Grid.Row="6" Grid.ColumnSpan="3" Header="View Actions" >
<GroupBox.Style>
<Style TargetType="GroupBox" d:DataContext="{d:DesignInstance ActionsViewModels:IActionViewModel}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsEnabled}" Value="False">
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</GroupBox.Style>
<ContentControl Content="{Binding ViewActionsVM}"/>
</GroupBox>
Just FYI, the is used to populate the GroupBox with objects which have a text and checkbox component inside them and look something like this
Now what I want to be able to do is when one unchecks this checkbox I want to be able to add a red border around the Groupbox in which its contained. The content shown in the image comes from the ContentControl binding that we have in our project. To add to the description of the Content, the content describes an Action to be performed and has a property called as IsEnabled that I am using in the DataTrigger to decide whether to add a border or not.
I've written a user control with popup, who's content is being set outside the control. The ControlTemplate of that control looks like the following:
<ControlTemplate TargetType="local:InfoIcon">
<Grid>
<ToggleButton x:Name="HelpButton" Style="{StaticResource HelpButton}" />
<Popup PlacementTarget="{Binding ElementName=HelpButton}" Placement="Bottom"
IsOpen="{Binding ElementName=HelpButton, Path=IsChecked, Mode=TwoWay}" StaysOpen="False">
<Border BorderBrush="#767676" BorderThickness="1"
Background="#f1f2f7">
<Border.Resources>
<!-- Important -->
<Style TargetType="Label">
<Setter Property="Foreground" Value="#575757" />
</Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="#575757" />
</Style>
<!-- /Important -->
</Border.Resources>
<ContentPresenter Content="{TemplateBinding HelpContent}" />
</Border>
</Popup>
</Grid>
</ControlTemplate>
The Important part - I want to assign custom styles to items, which are being put inside the popup (it serves as a clickable hint)
I'm using my control in the following way:
<local:MyControl>
<local:MyControl.HelpContent>
<TextBlock>Ala ma kota</TextBlock>
</local:MyControl.HelpContent>
</local:MyControl>
But despite styles in the Border, TextBlock's text's color always inherit the value from its parent (checked using Snoop) - resulting in white text on white background.
You can downlad the small PoC application, which demonstrates the problem.
My observations:
The styling does work for Label. It only doesn't work for TextBlock.
When I add TextBlock.Foreground="Red" to the Border, TextBlock becomes red, still ignoring style (but now using color from Border).
Snoop informs, that this TextBlock actually has the Style resolved correctly. But despite it shouldn't, it uses the inherited value instead of one specified in the style.
How can I solve this problem and why does it occur?
I received answer on Microsoft forums; I'll leave it here in case someone encounters the same problem.
The difference is that a TextBlock is not a control, i.e. it doesn't have any ControlTemplate and because of this the implicit style doesn't get applied to it when it is located inside the StackPanel. Please see the following page for more information: http://blogs.msdn.com/b/wpfsdk/archive/2009/08/27/implicit-styles-templates-controls-and-frameworkelements.aspx
You could use Label elements or set the style for the TextBlock elements explicitly.
-- Magnus (MM8)
Edit2
I've set the Foreground of the UserControl to something else. This behavior is because the child TextBlock controls of the UserControl inherit the Foreground-Settings somehow. This has nothing to do with the popup or some other approaches we tried yet.
I've stumbled upon another question with a similar problems here: Cannot override controls foreground colour in wpf
I suggest to accept this strange behavior and just set a Foreground Color of the UserControl instead:
<Style TargetType="{x:Type local:InfoIcon}">
<Setter Property="Foreground" Value="Red"/>
</Style>
previous Edit
You had my curiousity with this weird behavior, but after looking at your PoC it was rather obvious :) The Popup has some attached Properties TextElement.* where you can style the text elements in the popup. This was new to me, too and I will reseach a bit more afterwards. Nevertheless: Workaround for your Problem is to not style the TextBlock but the Popup instead. your code could look something like following :
<ControlTemplate TargetType="{x:Type local:InfoIcon}">
<ControlTemplate.Resources>
<Style TargetType="Popup">
<Setter Property="TextElement.Foreground" Value="Red"/>
</Style>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red" />
</Style>
</ControlTemplate.Resources>
<Grid>
<ToggleButton x:Name="TB" Width="16" Height="16"/>
<Popup Placement="Bottom" PlacementTarget="{Binding ElementName=TB}" IsOpen="{Binding ElementName=TB, Path=IsChecked}" StaysOpen="False">
<ContentPresenter Content="{TemplateBinding InfoContent}"/>
</Popup>
</Grid>
</ControlTemplate>
I changed the styles to be outside of the controls, of course you can just use the attached properties of the popup directly. But initially you wanted to know how it works with the styles attached at the border, it does not matter now where you add the styles. You can use a ResourceDictionary for example.
As a suggestion, shouldn't this:
TargetType="local:InfoIcon"
be like this?
TargetType="{x:Type local:InfoIcon}"
Maybe you have some TextBlock style defining that it shouldd take the parent's control foreground.
Did you try to add a BasedOn property like this ?
<Style TargetType="TextBlock" BasedOn="{StaticResource {x:Type TextBlock}}">
<Setter Property="Foreground" Value="#575757" />
</Style>
I tried with your code example and this works :
<ContentPresenter Content="{TemplateBinding InfoContent}">
<ContentPresenter.Style>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="TextBlock.Foreground" Value="Red" />
</Style>
</ContentPresenter.Style>
<ContentPresenter.Resources>
<Style TargetType="{x:Type Label}">
<Setter Property="Foreground" Value="Red" />
</Style>
</ContentPresenter.Resources>
</ContentPresenter>
That's kind of odd because when I put the Foreground setter for the Label control inside the ContentPresenter.Style then this time it's Label wich doesn't work...I think it's because Label is a considered as a ContentControl whereas TextBlock is just a FrameworkElement.
Had a similar issue caused by another problem:
There is a strange bug in WPF that prevents styles, defined in merged dictionaries, from being applied to the first element:
https://www.engineeringsolutions.de/wpf-fix-style-is-only-applied-to-first-element/
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.
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.