In a MVVM WPF application I have a border around an image:
<Border x:Name="PhotoBorder"
Background="Transparent"
Margin="3 0 5 0"
Grid.Row="3" Grid.RowSpan="7"
Grid.Column="0"
Height="110" Width="110"
HorizontalAlignment="Center"
CornerRadius="5"
BorderThickness="1"
BorderBrush="LightSteelBlue">
<Image x:Name="Photo"
Margin="3"
Height="{Binding ElementName=PhotoBorder, Path=Height-3}"
Width="{Binding ElementName=PhotoBorder, Path=Width-3}">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="{Binding Path=PhotoImg, Converter={StaticResource ImageConverter}}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=PhotoImg}" Value="{x:Null}">
<Setter Property="Source" Value="/Resources/Photo.png"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
</Border>
The problem is that black background is shown around the photo as below:
It is only happening in case view model property "PhotoImg" is null in the DataTrigger when attaching directly image "/Resources/Photo.png" to the "Source" property.
.. and I do not know why. Any ideas? Something related to the photo?
The original photo is:
Note: I am using Visual Studio 2008 and .NET Framework 3.5
Related
I've done the below code for a textbox which contains a image within it and I want to apply right padding at value of 20 so that the text does not overlap the icon, but it doesn't seem to work. I've tried it with the padding property existing only under the top <TextBox> or when it exists only within my <TextBox.Style> as well as both as per below code and none of these combinations work.
<TextBox x:Name="txtUser"
FontSize="13"
FontWeight="Medium"
FontFamily="Segoe UI"
Foreground="White"
CaretBrush="LightGray"
Height="28"
VerticalContentAlignment="Center"
Margin="0,5,0,0"
Padding="20,0,0,0">
<TextBox.Background>
<ImageBrush ImageSource="/Images/user-icon.png"
Stretch="None"
AlignmentX="Left"/>
</TextBox.Background>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="BorderBrush" Value="#5b69bc"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Setter Property="Padding" Value="20,0,0,0"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="#6e82ef"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Setter Property="Padding" Value="20,0,0,0"/>
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
I'm struggling to figure out why the padding would not apply in this scenario.
If you want the Background of your control to have padding, you can cheat a little with the VisualBrush. With it, you can "paint" using other UI elements.
For example, you can paint using a Grid with two columns. First column, with Width=20 is your "padding". In the second one there is a rectangle with your background image:
<TextBox
Name="txtUser"
Height="28"
Margin="0,5,0,0"
Padding="20,0,0,0"
VerticalContentAlignment="Center"
CaretBrush="LightGray"
FontFamily="Segoe UI"
FontSize="13"
FontWeight="Medium"
Foreground="White">
<TextBox.Background>
<VisualBrush AlignmentX="Left" Stretch="None">
<VisualBrush.Visual>
<Grid
Width="{Binding ElementName=txtUser, Path=ActualWidth}"
Height="{Binding ElementName=txtUser, Path=ActualHeight}"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="1">
<Rectangle.Fill>
<ImageBrush
AlignmentX="Left"
ImageSource="/Images/user-icon.png"
Stretch="None" />
</Rectangle.Fill>
</Rectangle>
</Grid>
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Background>
Notice that:
since the Visual in the VisualBrush knows nothing of its container, you have set its Width and Height (here, they're bound to the parent TetxBox's ActualHeight and ActualWidth)
The Background of the inner Grid is set (here: to Transparent). If you don't do this, the brush won't "see" the padding column anyway
EDIT:
If you're talking about Padding on the text itself, the code you provided as example works as expected, and the padding is applied properly.
In that case, you should either set the Padding directly or in Style, not both. If you set both, the ones in Style & Trigger will be ignored.
Following is the section from My Shell:
<StackPanel x:Name="stack" Orientation="Horizontal" Height="25" HorizontalAlignment="Right" Margin="0,4,0,0" Grid.Row="0">
<Button DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Path=DataContext}" Content="Back" prism:Click.Command="{Binding Path=GoBackCommand}"/>
<Button DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type StackPanel}}, Path=DataContext}" Content="Forward" prism:Click.Command="{Binding Path=GoForwardCommand}" Margin="10,0,0,0"/>
</StackPanel>
<ContentControl x:Name="ActionContent" prism:RegionManager.RegionName="{x:Static inf:RegionNames.WorkspaceRegion}" Grid.Row="1">
<ContentControl.Template>
<ControlTemplate TargetType="ContentControl" >
<Grid >
<Controls:RoundedBox/>
<ContentPresenter Margin="10,0,10,0" Content="{TemplateBinding Content}" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="false">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
<DataTrigger Binding="{Binding ElementName=stack}">
<Setter Property="DataContext" Value="{Binding RelativeSource={RelativeSource Self},
Path=Content.DataContext}" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
In ContentControl views are injecting from Modules (using ribbon tab). I want two button inside StackPanel may use ViewModels (DataContext) of injected views, for backward and Forward navigation.
Please Help, thanks!
I would start by having a good read at Prism Navigation Chapter,
and how to use the navigation journal.
Edit:
I don't think you can do what you want directly in XAML, but i can see 2 workarounds:
Define the commands as global CompositeCommand in the shell viewmodel, then have your views reister and unregister their own implementations as they are navigated.
Define a new Region in wich the views can inject their own navigation buttons.
Hope this can help you.
Branching off of this question -
When attaching a validation error template to my custom textbox like this -
<local:CustomTextBox CustomText="{Binding ViewModelProperty}" Validation.ErrorTemplate="{StaticResource errorTemplate}"/>
<ControlTemplate x:Key="errorTemplate">
<DockPanel>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder x:Name="controlWithError"/>
</Border>
<TextBlock Foreground="Red" FontSize="20" FontFamily="Segoe UI" Margin="3,0,0,0" MouseDown="Exclamation_MouseDown" Tag="{Binding AdornedElement.(Validation.Errors)[0].ErrorContent, ElementName=controlWithError}">!</TextBlock>
</DockPanel>
</ControlTemplate>
If there was a validation error in the ViewModelProperty, my application was throwing an exception -
Key cannot be null.
Parameter name: key
I'm not sure why this is happening. Is there something that needs to be done in order to assign a new error template to a custom control?
UPDATE:
I've figured out that the issue is with the Tag property in the error template. If I remove the Tag, it works just fine.
Thanks
Alright so the way I managed to fix the issue was by removing the AdornedElement keyword and changing the error template as follows:
<local:CustomTextBox CustomText="{Binding ViewModelProperty}">
<Validation.ErrorTemplate>
<ControlTemplate>
<DockPanel>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder x:Name="controlWithError"/>
</Border>
<TextBlock Foreground="Red" FontSize="20" FontFamily="Segoe UI" Margin="3,0,0,0" MouseDown="Exclamation_MouseDown">!</TextBlock>
</DockPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
<local:CustomTextBox.Style>
<Style TargetType="{x:Type local:CustomTextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
<Setter Property="Tag" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</local:CustomTextBox.Style>
</local:CustomTextBox>
What I don't understand is why it behaves differently when using the AdornedElement keyword but works fine when binding the Tag/Tooltip using the RelativeSource. While the problem is solved, I would welcome any ideas as to why this happened.
Thanks
I use IDataErrorInfo on my viewmodels and I have a style (below) for a TextBox with an error template that works ok. I know that "ValidatesOnDataErrors=True" used like:
<TextBox Text="{Binding Path=LastName, ValidatesOnDataErrors=True}"
Style="{StaticResource TextBoxStyle}" />
will force WPF to use IDataErrorInfo but am wondering how to get that baked into my style.
Cheers,
Berryl
<Style x:Key="TextBoxStyle" TargetType="{x:Type TextBox}">
...
<!--
Error handling
-->
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right" Text=" *"
Foreground="Red"
FontWeight="Bold" FontSize="16"
ToolTip="{Binding ElementName=placeholder, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
<Border BorderBrush="Red" BorderThickness="1">
<AdornedElementPlaceholder Name="placeholder"></AdornedElementPlaceholder>
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="Background" Value="LightYellow"/>
</Trigger>
</Style.Triggers>
</Style>
If I understand what you're asking, you want to be able to be able to use the ValidatesOnDataError=True in your style, that so you don't have to repeat it every time.
If that's the case you can't, because that is a property of the data binding and not the style; and you can't template the data binding.
I just wonder if you use a Label instead of a TextBox, then in the style of the Label you can probably do something like this,
<ControlTemplate TargetType="sdk:Label">
<TextBlock x:Name="textBlock" Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}, ValidatesOnDataErrors=True}">
You can't because that is part of the definition of the binding to your property, not how the error is visualized.
I'm learning WPF and can't figure out how to enfore my buttons to take a square shape.
Here is my XAML Markup:
<Window x:Class="Example"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="368" Width="333">
<Window.Resources>
<Style x:Key="ToggleStyle" BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="{x:Type RadioButton}">
</Style>
</Window.Resources>
<RadioButton Style="{StaticResource ToggleStyle}">
Very very long text
</RadioButton>
</Window>
Specifying explicit values for Width and Height attributes seems like a wrong idea - the button should calculate its dimensions based on its contents automagically, but keep its width and height equal. Is this possible?
Try this - it seems to work in Kaxaml:
<Button
MinWidth="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"
MinHeight="{Binding ActualWidth, RelativeSource={RelativeSource Self}}">
Some content
</Button>
(To test, I put a TextBox inside the button, because that's an easy way to change content size without re-parsing the Xaml.)
Edit: sorry, should probably have specified it as a style to match your example:
<Style TargetType="Button" x:Key="SquareButton">
<Setter Property="MinWidth" Value="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" />
<Setter Property="MinHeight" Value="{Binding ActualWidth, RelativeSource={RelativeSource Self}}" />
</Style>
The currently accepted answer cannot grow AND shrink. I think I found a better approach, by defining a custom style with a rectangle (stretched uniformly).
<Style x:Key="SquareButton" TargetType="Button">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="#3a3a3a" />
<Setter Property="BorderBrush" Value="#5a5a5a"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border HorizontalAlignment="Center">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Rectangle Stretch="Uniform"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="4"
Margin="0"
Fill="{TemplateBinding Background}"
Panel.ZIndex="1"
/>
<ContentPresenter Margin="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Panel.ZIndex="2" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#4a4a4a"/>
</Trigger>
</Style.Triggers>
</Style>
Btw, you can also create a perfect round button this way. Just replace Rectangle with Ellipse.
I think you want to bind button's width to its height, like this:
<Button Name="myButton"
Width="{Binding ElementName=myButton, Path=Height}"
Height="100">
Button Text
</Button>
#Dan Pruzey's answer was good, but did not shrink the square if the window was downsized. What I found to work best was to set the height to auto and the width to that height:
<Button Height="Auto" Width="{Binding ActualHeight, RelativeSource={RelativeSource Self}}"/>
This kept the button a square when sizing my window bigger and smaller. Hope this helps someone.
I found a simpler solution for the case where you want the button to expand and shrink, tipically when its text changes:
<RadioButton Style="{StaticResource ToggleStyle2}"
HorizontalAlignment="Center" VerticalAlignment="Center"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
Width="{Binding Path=ActualWidth, ElementName=txtblck_innerText }"
Height="{Binding Path=ActualWidth, ElementName=txtblck_innerText }">
<TextBlock x:Name="txtblck_innerText"
MinWidth="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" >
Very very long text.........
</TextBlock>
</RadioButton>
This solution above applies when the size of the button is imposed by its content (comes from inside) as stated in the question.
However, in case you want a square button whose size is imposed by the place where it is going to be (comes from outside), for instance, the size of the grid cell that contains the button, a solution could be:
<RadioButton Style="{StaticResource ToggleStyle2}"
HorizontalAlignment="Center" VerticalAlignment="Center"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
<Viewbox Stretch="Uniform">
<Border Width="20" Height="20">
<Viewbox Stretch="Uniform">
<TextBlock x:Name="txtblck_innerText">
Very very long text...........
</TextBlock>
</Viewbox>
</Border>
</Viewbox>
</RadioButton>