I'm getting a visual studio exception at run time and I have no idea where it's coming from!
I am creating a recipe book and meal planner desktop app in WPF. I'm currently working on the interface that allows users to input ingredients into a recipe.
I have a user control for each of the individual Ingredients, which I dynamically add to a stack panel in a Recipe user control. The stack panel is wrapped in a ScrollViewer using the code below:
<ScrollViewer Grid.Row="3" VerticalScrollBarVisibility="Auto" Margin="20,10,0,0">
<StackPanel Name="IngredientsPanel">
<!--IngredientUserControl objects are dynamically added here-->
</StackPanel>
</ScrollViewer>
Adding ingredients works until the ingredients fill the visible space in the stack panel. At that point, instead of a scrollbar appearing, I get the following exception:
System.Windows.Markup.XamlParseException: ''{DependencyProperty.UnsetValue}' is not a valid value for
property 'Foreground'.'
This exception was originally thrown at this call stack:
[External Code]
I suspect this is an issue with some of the styles I've defined to format my text boxes and text blocks within the IngredientUserControl. I keep these styles in a Fonts.xaml resource dictionary which is included in my App.xaml file. My font styles all look roughly like this:
<!--Body1 text (used for static large body text)-->
<Style x:Key = "Body1Text" TargetType = "TextBlock">
<Setter Property="VerticalAlignment" Value="Stretch"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="TextAlignment" Value="Left"/>
<Setter Property="FontSize" Value="16"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryBrush}"/>
<Setter Property="FontFamily" Value="Comic Sans MS"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Margin" Value="5"/>
</Style>
PrimaryBrush is a SolidColorBrush which I define differently in the resources section of all my user controls (I do this because the color theming varies across different components of my app, but the font styles do not). For example, inside my IngredientUserControl I have these resources:
<UserControl.Resources>
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource OrangeTheme}"></SolidColorBrush>
<SolidColorBrush x:Key="AccentBrush" Color="{StaticResource LightTealTheme}"></SolidColorBrush>
</UserControl.Resources>
And this text block:
<TextBlock Name="Quantity" Margin="0,20,0,5"
Text="{Binding Path=Quantity, Mode=OneWay}"
Style="{StaticResource Body1Text}">
</TextBlock>
All of the styles work and display correctly until the Ingredients Panel runs out of space! When the ScrollViewer gets full, it breaks and can no longer find some foreground property somewhere. I don't understand why I'm only having this issue when the ScrollViewer needs to scroll. Any ideas?
Turns out it was an issue with the style on the ScrollViewer. I had not even attempted to style the ScrollViewer, but whatever default style it was trying to apply was missing a resource. Very weird. I built my own style for the ScrollViewer and now it's working correctly.
Related
I have a very basic custom control consisting of a Label and a Textbox. I've used my control for sometime without any issues.
I've now come to styling my application and have my style inside a XAML file containing just a ResourceDictionary. I have the following for my UserControl:
<Style TargetType="local:LabelEdit">
<Setter Property="Background" Value="{StaticResource BackgroundBrush}" />
<Setter Property="Foreground" Value="{StaticResource ForegroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource BorderBrush}" />
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder />
<Image Source="/AJSoft.Controls;component/Resources/Icons/cross.ico" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Foreground" Value="{StaticResource ErrorForegroundBrush}" />
<Setter Property="Background" Value="{StaticResource ErrorBackgroundBrush}" />
<Setter Property="BorderBrush" Value="{StaticResource ErrorBorderBrush}" />
</Trigger>
</Style.Triggers>
</Style>
Everything works absolutely fine if I comment out the Setter for Validation.ErrorTemplate. If the ErrorTemplate is left intact, the cross shows (I haven't sorted out placement yet, but that can come later...), but the Textbox component of my UserControl does not show the caret or accept keyboard input. The rest of the controls in my Window work as expected.
Here are some screenies where I've deliberately put in some erroneous text to show how it looks.
The same problem happens even if I change that huge image to be a textblock with a small red "!" - the image is just for effect for now.
What am I doing that's causing the problem? I'm new to Validation in WPF...
EDIT: The image shown (big red cross) is just one example of what I've done. Even if I use a small image shown alongside the UserControl, I still get the same effect.
If you were to look at how error templates usually work, you'd see they apply to a single control.
Part of the issue you have here is you've got a label and textbox in one parent user control.
If you then apply an error template at the usercontrol level, it's on everything in that. Label, textbox, everything in your usercontrol.
The next thing to consider is how your error template ends up visible on top of everything. This happens because your error template generates UI in the adorner layer. That's top of everything ( in the window ).
Add these together and you got a big image on top of the content of your usercontrol.
At risk of over simplifying:
You put a top on your box and you can't now get at what's in that box.
There are several ways you could "fix" this but they all involve some design change or compromise.
Maybe a big X on top of your input control isn't a good idea.
You could kind of make it work.
You could make your image IsHitTestVisible="False".
It'll be visually in the way but you can then likely click on the textbox and type.
Just maybe not see everything.
Probably not ideal.
You could show your cross next to the textbox using a datatrigger rather than error template.
Add an image to your usercontrol so you have label, textbox, CrossImage.
Add a style to that with a setter makes it visible collapsed by default.
Use a trigger in that style to show the CrossImage when the control has errors.
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter ... />
</Trigger>
</Style.Triggers>
You may well find it simplest to use the tag on the usercontrol and set that to visible/collapsed. Bind visibility of the image to that.
I use Margin="3" and Padding="5,1,5,1" with my buttons in wpf. I realized if I set a shortcut key like this:
<Button Content="_Decide types" Command="{Binding DecideTypesCommand}"
ToolTip="Select rows before pressing this button!"/>
Then this button will increase. It seems the _ uses some extra padding. Can I control this somehow?
Update:
The Margin and Padding is set in App.xaml. E.g. the "Create type hint" button uses this settings properly. The "First" button is clearly bigger than the "Create type hint" button. If I remove the shortcut from the "First" button then its size will be normal.
Update 2: Here you can see a WrapPanel and below it the corresponding style in App.xaml. You can see on the second figure that first button is bigger and the only difference is the _. I tried to recreate the problem in an empty project but there the two button had the same size.
<WrapPanel>
<Button Content="_Decide types"/>
<Button Content="Decide types"/>
</WrapPanel>
<Style TargetType="Button">
<Setter Property="Margin" Value="3"/>
<Setter Property="Padding" Value="5,1"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
Even using update 2, I don't get the same results as you. Without having access to all of your XAML and styles, it's impossible to tell what might be causing this issue.
With that said, I recommend that you standardize the height of your buttons so they are all consistent in your app. Since you already have an implicit Button style, you might want to add a setter for the Height property.
Something like this:
<Style TargetType="Button">
<Setter Property="Margin" Value="3"/>
<Setter Property="Padding" Value="5,1"/>
<Setter Property="Height" Value="24"/> <-- whatever looks good for your app
</Style>
I realized that there was a style which was active (not in App.xaml), see below. If I delete this style then the size difference disappear between a button with and without shortcut. I have no idea how this is connected to a TextBlock style.
Thank you for the hints.
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="Margin" Value="3"/>
</Style>
</Window.Resources>
Suppose I have a WPF style for a container element such as a grid which applies styles to its child items automatically, like this:
<Window.Resources>
<Style TargetType="Grid" x:Key="FormStyle">
<Style.Resources>
<Style TargetType="Label">
<Setter Property="FontSize" Value="50"/>
</Style>
</Style.Resources>
</Style>
</Window.Resources>
How can I then override certian elements of that style within the grid itself? For example suppose I wanted one grid to have FormStyle but also have a blue label, like this (which doesnt work):
<!-- this works fine and Label size = 50 -->
<Grid Style="{StaticResource FormStyle}">
<Label Content="Blah"/>
</Grid>
<!-- But this doesnt, label is blue, but normal font size -->
<Grid Style="{StaticResource FormStyle}">
<Grid.Resources>
<Style TargetType="Label" BasedOn="{StaticResource {x:Type Label}}">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</Grid.Resources>
<Label Content="Blah"/>
</Grid>
I am expecting the BasedOn={StaticResource {x:Type Label}} to refer to the current active style for Labels at the current scope - i.e. the label style within FormStyle. But it clearly doesnt and refers to the base outer label style.
If I do for instance this globally
<Style TargetType="Label">
<Setter Property="FontSize" Value="50"/>
</Style>
Then it is all fine.
I could of course just name the styles, but surly there must be an easier/less verbose way?
Thanks
Here is the lookup process for Static Resources:
The lookup process checks for the requested key within the resource
dictionary defined by the element that sets the property.
The lookup process then traverses the logical tree upward to the
parent element and its resource dictionary. This process continues
until the root element is reached.
App resources are checked. App resources are those resources within
the resource dictionary that is defined by the Application object
for your WPF app.
In your case to resolve BasedOn="{StaticResource {x:Type Label}}" WPF first looks in the ResourceDictionary defined inside of Grid, then in Window - the logical parent of the Grid - and it's Resources, and then finally in Application level resources. WPF will not find it anywhere - and defaults to the base style - due to the style being a nested style in FormStyle.
Read further about Static Resource Lookup Behavior on Docs.
To get the desired output, you could:
1) Move your Label style out of FormStyle and in to Window.Resoruces
2) Merge the Label style from FormStyle into the Label style defined in the Grid element.
<Grid Style="{StaticResource FormStyle}">
<Grid.Resources>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Red"/>
<Setter Property="FontSize" Value="50"/>
</Style>
</Grid.Resources>
<Label Content="Blah"/>
</Grid>
3) Change your FormStyle not to have a nested style for Label, but to have setters for Label properties.
<Window.Resources>
<Style TargetType="Grid" x:Key="FormStyle">
<Setter Property="Label.FontSize" Value="50"/>
</Style>
</Window.Resources>
My problem:
I haven't been able to figure out how I inside the 'Setter.Value' field of a setter for a property A in a style targeting a particular control can do a binding to a property B of that particualr control. More specifically I want to use the Foreground brush value on a graphical element inside the visual tree of the Content property of a Button. this will ensure that the graphical element always has the foreground color set for this button control.
What I try to achive:
I'm working on a WPF-application where I have three button controls:
DefaultButton
SpecialButton
ExtendedSpecialButton
The DefaultButton is where I define the style of buttons in the application through a style with a ControlTemplate.
The SpecialButton introduces a new property not supposed to be used for general buttons. This property will be represented by one visual state that I define through a style setter. Else from that it shall be identical in apperance to the DefaultButton.
I define the style of this SpecialButton by basing it on the style of the DefaultButton. In this style there is no ControlTemplate only a MultiTrigger-response on the basis of a couple of property conditions setting av a couple of visual properties:
<Style x:Key="SpecialButtonStyle" TargetType="{x:Type MyControls:SpecialButton}" BasedOn="{StaticResource DefaultButtonStyle}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsActive" Value="false"/>
<Condition Property="IsMouseOver" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="BorderBrush" Value="{DynamicResource ButtonDisabledBorder}" />
<Setter Property="Background" Value="{DynamicResource ButtonDisabledBg}" />
</MultiTrigger>
</Style.Triggers>
</Style>
All this worked great.
The next step is also no problem:
I wanted to base the ExtendedSpecialButton on the SpecialButton and set a default shape content inside the button.
<Style x:Key="ExtendedSpecialButtonStyle" TargetType="{x:Type MyControls:ExtendedSpecialButton}" BasedOn="{StaticResource SpecialButtonStyle}">
<Setter Property="Content">
<Setter.Value>
<Rectangle Fill="Black" Height="5" Width="15"></Rectangle>
</Setter.Value>
</Setter>
</Style>
The original style of DefaultButton is still present - the added visual state responding to the IsActiveProperty of the SpecialButton is still with us - and the ExtendedSpecialButton also inherited the visual behaviour created by the MultiTrigger of the SpecialButton.
I also successfully displayed a graphical element that this ExtendedSpecialButton should have.
However I wanted the fill of this graphical element to use the Foreground color. This foreground color is originally styled in the DefaultButton and works just fine for the two first buttons.
The code below is how I currently thought such a binding should be done. But this does not work:
<Style x:Key="ExtendedSpecialButtonStyle" TargetType="{x:Type MyControls:ExtendedSpecialButton}" BasedOn="{StaticResource SpecialButtonStyle}">
<Setter Property="Content">
<Setter.Value>
<Rectangle Fill="{Binding RelativeSource={RelativeSource TemplatedParent}}" Height="6" Width="20"></Rectangle>
</Setter.Value>
</Setter>
</Style>
Does anyone know what I could do to set up the binding so that it does what I intended it to do?
I have a resource dictionary in my WPF application which contains the style information for the various controls.
Can it be used like the way we use in CSS in HTML? For example
p
{
margin:20px;
font:Tahoma;
}
this applies to all "p" tags in HTML. We dont have to specifically mention that in the HTML for "p" tag.
Is the same approach applicable in WPF, or do we have to specifically
mention the style
<TextBlock Text="Test" Style="{DynamicResource SomeTextblockStyle}" />
in the XAML
You can certainly set a default style for each type. You can do this within your Generic.xaml, note that I am not providing a key.
<Style TargetType="{x:Type Button}">
<Setter Property="Height" Value="25"/>
<Setter Property="Foreground" Value="White"/>
</Style>
This will style every instance of a Button within your application as such.
If you were go to a XAML file and define an instance of a Button, overriding the Foreground value, that local instance will take precedence over the global style.
<Button Foreground="Black"/>
You can set style like using key
<Style TargetType="{x:Type TextBlock}" x:Key="myStyle">
<Setter Property="Margin" Value="20"/>
<Setter Property="FontFamily" Value="Tahoma"/>
</Style>
And in the Window.Xaml
<TextBlock Text="Hello" Style="{DynamicResource myStyle}"/>