How to set GridViewColumn Cell Template with Style Triggers - wpf

At the moment my grid is changing to Autogenerate columns so I cannot predefine the column style. I'm wondering how abouts do I set a style trigger for a column based on header value.
Working code with predefined columns with AutoGenerateColumns="True"
<telerik:GridViewColumn Header="Test">
<telerik:GridViewColumn.CellEditTemplate>
<DataTemplate x:Key="ToggleDataTemplate">
<telerik:RadToggleButton Content="+" Width="20" Height="20"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"/>
</DataTemplate>
</telerik:GridViewColumn.CellEditTemplate>
</telerik:GridViewColumn>
What I've tried with AutoGenerateColumns="False"
<telerik:RadGridView.Resources>
<Style TargetType="telerik:GridViewColumn">
<Style.Triggers>
<Trigger Property="Header" Value="Test">
<Setter Property="CellTemplate" Value="{StaticResource ToggleDataTemplate}" />
<Setter Property="CellEditTemplate" Value="{StaticResource ToggleDataTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
</telerik:RadGridView.Resources>
<DataTemplate x:Key="ToggleDataTemplate">
<telerik:RadToggleButton Content="+" Width="20" Height="20"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"/>
</DataTemplate>
With what I tried above, the cell remains blank. How abouts do I get the button to appear dynamically?

You should be able to handle the AutoGeneratingColumn event and set the CellEditTemplate of all columns programmatically:
<telerik:RadGridView x:Name="grid" AutoGeneratingColumn="grid_AutoGeneratingColumn">
<telerik:RadGridView.Resources>
<DataTemplate x:Key="ToggleDataTemplate">
<telerik:RadToggleButton Content="+" Width="20" Height="20"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"/>
</DataTemplate>
</telerik:RadGridView.Resources>
</telerik:RadGridView>
private void grid_AutoGeneratingColumn(object sender, Telerik.Windows.Controls.GridViewAutoGeneratingColumnEventArgs e)
{
e.Column.CellEditTemplate = grid.Resources["ToggleDataTemplate"] as DataTemplate;
}

Related

Using togglebutton for RowDetailsTemplate

Say I want to use RowDetailsTemplate in my WPF.
<Grid x:Name="LayoutRoot" Background="White">
<data:DataGrid x:Name="grdVwDetails" AutoGenerateColumns="False" RowDetailsVisibilityMode="Collapsed">
<data:DataGrid.RowDetailsTemplate>
<DataTemplate>
<data:DataGrid HeadersVisibility="None" ItemsSource="{Binding Path= Project}" AutoGenerateColumns="True">
</data:DataGrid>
</DataTemplate>
</data:DataGrid.RowDetailsTemplate>
<data:DataGrid.Columns>
<data:DataGridTemplateColumn>
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="+" Click="HandleExpandCollapseForRow"></Button>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
<data:DataGridTextColumn Header="EmployeeID" Binding="{Binding EmpID}"></data:DataGridTextColumn>
<data:DataGridTextColumn Header="Name" Binding="{Binding Name}"></data:DataGridTextColumn>
<data:DataGridTextColumn Header="Address" Binding="{Binding Address}"></data:DataGridTextColumn>
</data:DataGrid.Columns>
</data:DataGrid>
</Grid>
In code behind I use event to change the button appearance.
private void HandleExpandCollapseForRow(object sender, RoutedEventArgs e)
{
Button expandCollapseButton = (Button)sender;
DataGridRow selectedRow = DataGridRow.GetRowContainingElement(expandCollapseButton);
if (null != expandCollapseButton && "+" == expandCollapseButton.Content.ToString())
{
selectedRow.DetailsVisibility = Visibility.Visible;
expandCollapseButton.Content = "-";
}
else
{
selectedRow.DetailsVisibility = Visibility.Collapsed;
expandCollapseButton.Content = "+";
}
}
It seems work. My question is that I don't want to use +/- for button. I want to use different image for expand/collapse.
And do it should be done in xaml instead of code behind.
The entire project can be found at http://www.a2zmenu.com/Blogs/Silverlight/Collapse-RowDetailstemplate-on-clicking-again.aspx
That is for Silverlight, I use it for WPF.
You could replace the Button in the DataTemplate with a ToggleButton with a custom template:
<ToggleButton Width="50" Height="50">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<Image x:Name="img" Source="plus.png" />
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="img" Property="Source" Value="minus.png" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>

Displaying an alternative text when no image has been assigned yet

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.

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.

Closing TabItem by clicking middle button

I have a problem.
In my WPF application, if i press a tabItem with middle mouse button, this tabItem should close. Just like in FireFox.
But I try to do this using MVVM, and i need to use commands. Also my tabItems are created dynamically.
Help me plz!
Thank you!
Create a DataTemplate for your tab items like this:
<DataTemplate x:Key="ClosableTabTemplate">
<Border>
<Grid>
<Grid.InputBindings>
<MouseBinding Command="ApplicationCommands.Close" Gesture="MiddleClick" />
</Grid.InputBindings>
<!-- the actual contents of your tab item -->
</Grid>
</Border>
</DataTemplate>
In your application window, add a the close command
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close" Executed="CloseCommandExecuted" CanExecute="CloseCommandCanExecute" />
</Window.CommandBindings>
and finally assign the data template as item template to your tab control.
You could add a MouseBinding to some InputBindings perhaps?
Closing a TabItem (Not Selected) - TabControl
<TabControl x:Name="TabControlUser" ItemsSource="{Binding Tabs}" Grid.RowSpan="3">
<TabControl.Resources>
<Style TargetType="TabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<Border Name="Border" BorderThickness="1,1,1,0" BorderBrush="Gainsboro" CornerRadius="4,4,0,0" Margin="2,0">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="10,2"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="Border" Property="Background" Value="LightSkyBlue" />
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="Border" Property="Background" Value="GhostWhite" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabControl.ItemTemplate>
<!-- this is the header template-->
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header}" FontWeight="ExtraBold" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Image Source="/Images/RedClose.png" Width="22" Height="22" MouseDown="Image_MouseDown" HorizontalAlignment="Right" Margin="10 0 0 0"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!-- this is the body of the TabItem template-->
<DataTemplate>
<UserControl Content="{Binding UserControl, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
C# Code - Image MouseDown Click Event
try
{
int matches = 0;
DependencyObject dependency = (DependencyObject)e.OriginalSource;
// Traverse the visual tree looking for TabItem
while ((dependency != null) && !(dependency is TabItem))
dependency = VisualTreeHelper.GetParent(dependency);
if (dependency == null)
{
// Didn't find TabItem
return;
}
TabItem selectedTabItem = dependency as TabItem;
var selectedTabContent = selectedTabItem.Content as MainWindowViewModel.TabItem;
foreach (var item in MainWindowViewModel.Tabs)
{
if (item.Header == selectedTabContent.Header)
{
matches = MainWindowViewModel.Tabs.IndexOf(item);
}
}
MainWindowViewModel.Tabs.RemoveAt(matches);
}
catch (Exception ex)
{
System.Windows.Application.Current.Dispatcher.Invoke((System.Action)(() => new View.MessageBox(ex.Message).ShowDialog()));
}
This event finds with the exact Mouse clicked position (as TabItem)and this will remove
MainWindowViewModel.Tabs.RemoveAt(matches); the TabItem(even if it is not selected).

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.

Resources