Displaying an alternative text when no image has been assigned yet - wpf

I have an image control on which the user can drag and drop pictures. When no image has been dropped in there and the control is empty, I'd like to show an alternative text such as "Drop a picture here" to better indicate what is expected from him/her.
I can't figure out how to use triggers with this, this shows nothing at all and I can no longer drop pictures on my image control:
<StackPanel Grid.Column="1" Grid.Row="2" FlowDirection="LeftToRight" Orientation="Horizontal" Margin="0px 4px">
<StackPanel.Resources>
<DataTemplate x:Key="tmpTemplate">
<Border BorderThickness="2" BorderBrush="#FF969DFF" CornerRadius="2" VerticalAlignment="Top">
<DockPanel>
<Image Name="imgSelectedViewImage" Source="{Binding Image}" MinWidth="32" MinHeight="32" MaxWidth="48" MaxHeight="48"
HorizontalAlignment="Left" Stretch="None"
IsEnabled="{Binding EditMode}" Margin="2px"/>
<Label Content="Drag here" Name="AltText" Visibility="Collapsed"></Label>
</DockPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Image}" Value="{x:Null}">
<Setter TargetName="AltText" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</StackPanel.Resources>

I can't tell you what you've done to break your previously working drag and drop functionality and all I can do is to suggest you undo your changes to remedy that. However, if you want to display something when a collection is empty, then there's an easy way to do that. Using the collection's Count property is no good because it won't update when items are added or removed.
You can simply add an int property next to your item collection for this, but you have to make sure that you notify the INotifyPropertyChanged interface for it also when the collection property changes:
public ObservableCollection<YourItem> YourItems
{
get { return yourItems; }
set
{
yourItems = value;
NotifyPropertyChanged("YourItems");
NotifyPropertyChanged("YourItemsCount");
}
}
public int YourItemsCount
{
get { return YourItems.Count; }
}
Then you can use it in a simple DataTrigger like this:
<Grid>
<!-- Put your normal content here -->
<TextBlock FontSize="16" HorizontalAlignment="Center" VerticalAlignment="Center"
Text="Drop images here">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding YourItemsCount}" Value="0">
<Setter Property="TextBlock.Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>

I did it by setting the TargetNullValue and the FallbackValue to a static resource. See the code snippet below. You can of course set it to everything else you want.
<Image Stretch="Uniform"
Grid.Row="0"
Grid.Column="1"
MaxHeight="250"
MaxWidth="250"
Margin="10"
Source="{Binding Path=Character.Portrait, TargetNullValue={StaticResource FallbackImage}, FallbackValue={StaticResource FallbackImage}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<cal:ActionMessage MethodName="ChangePicture">
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</image>
Another approach, which could be more to your liking, as it uses triggers could be this solution: Handling null when binding to an Image in XAML (look at the accepted answer). However in my opinion setting TargetNullValue and the FallbackValue is the best way to go.

Related

Possibility of displaying content based on logic using XAML for WPF

In an application which uses WPF (XAML) along with the MVVM pattern I have the following piece of XAML code which works just fine,
< DataGridTemplateColumn Header="Status">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Visibility="{Binding Path=IsCompleted, Converter={BoolToVisibilityConverter}}">
<Path Data=" "
Fill="# "
Stretch=" "
Height=""
Width=""/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
The boolean property "IsCompleted" is defined in the corresponding view model which gets populated with "true" or "false". This functionality works as expected up until this point which is when the boolean property is true the expected icon is visible but the improvement I'm hoping to make is to introduce a logic where when the boolean property is set to false as it does right now, instead of having a blank space in the area (since the icon wont be visible) to display a string or text block instead, something like "In complete".
Is it possible to do this from the xaml side or should I make some changes in the view model?
You could add a DataTrigger to the DataTemplate that sets the Visibility property of the Path and/or a TextBlock, e.g.:
<DataTemplate>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
<Path x:Name="path"
Data=" "
Fill="# "
Stretch=" "
Height=""
Width="" Visibility="Collapsed"/>
<TextBlock x:Name="tb" Text="InComplete" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsCompleted}" Value="True">
<Setter TargetName="tb" Property="Visibility" Value="Collapsed" />
<Setter TargetName="path" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>

Create combobox with different items design using data trigger

I have defined the names and color in the backend using C# so I want to select the item cat and change it's other elements.
You don't show the XAML you are using to set up the ComboBox but best approach is to use a data template to set up the ComboBox items however you wish them to look. If you want the color box to not be shown if the Color brush is null, you could then add a trigger to the template to make it Hidden. Note that if you make it collapsed it will realign the layout of the other controls.
<DataTemplate x:Key="cbDataTemplate">
<StackPanel Orientation="Horizontal">
<Rectangle x:Name="clrBox" Fill="{Binding Color, TargetNullValue=Transparent}"
Stroke="Black" StrokeThickness="2" Width="16"/>
<TextBlock Text="{Binding Name}" Margin="10,0,0,0"/>
</StackPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Color}" Value="{x:Null}">
<Setter TargetName="clrBox" Property="Visibility" Value="Hidden"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
then to use this DataTemplate simply add it to the ComboBox like this:
<ComboBox ItemTemplate="{StaticResource cbDataTemplate}" Width="150"/>
hope that's helpful...

ListBox item template depends on item values

In WPF, MVVC applicaiton I have quite normal listbox:
<ListBox ItemsSource="{Binding Friends}">
</ListBox>
Then each item of list uses following datatemplate:
<DataTemplate DataType="{x:Type model:Friend}">
<Border BorderThickness="1" BorderBrush="Gray" Padding="3" Name="border" Margin="3">
<StackPanel Grid.Row="1" Grid.Column="2" Orientation="Horizontal">
<TextBlock Name="DescriptionDTDataType" Text="{Binding Path=ConnectedUserID}" />
<TextBlock Name="StatusTitle" Margin="8,0,4,0">Status:</TextBlock>
<TextBlock Name="Status" Text="{Binding Path=Approved}" />
<Button Content="Approve" Command="{x:Static inf:Commands.ApproveUserConnection}" CommandParameter="{Binding}"></Button>
<Button Content="Disapprove" Command="{x:Static inf:Commands.DisapproveUserConnection}" CommandParameter="{Binding}"></Button>
</StackPanel>
</Border>
</DataTemplate>
The question is... I want to hide one of the buttons on the basic of Friend.Approved property. For example if Friend.Approved has value "approved" I want to hide button Approved and show only button Dissaprove. On the other hand if Friend.Approved has value "disapproved" then I want opposite. How to achieve this?
Thank you.
Beside creating a IValueConverter there is a pure XAML solution using DataTriggers
Just add this to your DataTemplate:
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Approved}" Value="approved" >
<Setter TargetName="Approve" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding Approved}" Value="disapproved" >
<Setter TargetName="Disapprove" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
And name your buttons:
<Button Name="Approve" Content="Approve" ...></Button>
<Button Name="Disapprove" Content="Disapprove" ...></Button>
However if you want to reuse this hiding logic in multiple places in multiple DataTemplates its better to write the Approved text to Visibility converter.
Bind the Buttons Visibility property to Approved and use a value converter to convert Approved to a Visibility value.

XAML controls binding with another controls which are in datatemplate !

i have the following datatemplate in xaml and here i have textbox in datatemplate and button in normal XAML, what i really want is that i want to keep button be disabled until person enters something in textbox ? but its not working i have tried the follwing code
Please look into it and help me as well ! or is there any other way to do this let me know !
<UserControl.Resources>
<DataTemplate x:Key="dataTemplateParameter">
<StackPanel Width="170" Height="Auto" Margin="5">
<TextBlock Width="160" TextAlignment="Left" HorizontalAlignment="Left" Height="22" Text="{Binding Path=ParameterName, Mode=TwoWay}"/>
<TextBox x:Name="txtboxParameter" Width="160" Height="22" HorizontalAlignment="Left" Text="{Binding Path=ParameterValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</DataTemplate>
<Button Grid.Row="6" Name="SearchSourceParamBtn" Content="Search" VerticalAlignment="Bottom" Margin="12,5,92,0" Height="20" Visibility="{Binding SearchSourceBtnVisibility}" Command="{Binding SearchCommand}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Text,x:ElementName=txtboxParameter}" Value="">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
Thanks
nallskarthi
You can achieve this by
1) binding button to RelayCommand. Then,
2) Validate the "ParameterValue" in command's CanExecute delegate.
Edit:
The better way is to have the ParameterValue in ViewModel, but for some reason you won't do that. Then, create a static ChangeNotifyable property in VM, using this property do the validation in CanExecute delegate.
Pls Validate the "ParameterValue" in the CanExecute delegate of Command object.

WPF Bind to parent property from within nested element using style

I've been trying to build a text box with a hint that's displaying while it's empty.
I'm having trouble setting the hint text from within a style.
To be precise, this works (that is, it binds correctly):
<TextBox Tag="hint text">
<TextBox.Background>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=TextBox}}" FontStyle="Italic" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</TextBox.Background>
</TextBox>
but, when I move it to the Style, it doesn't:
<Style TargetType="TextBox" x:Key="stlHintbox">
<Style.Triggers>
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Mode=Self}}" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Tag="inner" Text="{Binding Tag, RelativeSource={RelativeSource AncestorType=TextBox}}"
FontStyle="Italic" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
<TextBox Tag="hint text" Style="{StaticResource stlHintbox}" />
So what's the catch? How can I bind to ancestor property from within a style?
The problem is not with the RelativeSource but with the way you are using the VisualBrush. Recall that Styles are shared between the elements you apply them to. The reason that your example doesn't work is that, in effect you are trying to share a single textbox (the one you tagged "inner") with multiple parent textboxes.
To see why this is a problem, try a thought experiment: The inner textbox gets created once (roughly speaking, this will happen when the style is created). Which of the textboxes that the style gets applied to should be chosen as the ancestor of the inner text box when you use the RelativeSource binding?
This is why DataTemplates and ControlTemplates exist in WPF. Rather than actually instantiate visuals directly, they define a template that allow multiple copies of visuals to be created as needed.
Reativesource doesn't work as expected.
It is better to create watermark textbox using control template. But your version could work:
<Window.Resources>
<Style TargetType="TextBox" x:Key="stlHintbox">
<Style.Triggers>
<DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Mode=Self}}" Value="">
<Setter Property="TextBox.Background">
<Setter.Value>
<VisualBrush Stretch="None" Visual="{Binding ElementName=hintText}"/>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<TextBox Tag="hint text" x:Name="myTextBox" Style="{StaticResource stlHintbox}" />
<Border Visibility="Hidden">
<TextBlock x:Name="hintText" Text="{Binding Tag, ElementName=myTextBox}" FontStyle="Italic" Foreground="LightGray" />
</Border>
</StackPanel>

Resources