Set a Usercontrol as a Datatemplate - wpf

I have the following ProductList template snipet
<Style x:Key="ProductListStyle" TargetType="{x:Type s:SurfaceListBox }">
<Setter Property="Background" Value="{DynamicResource {x:Static s:SurfaceColors.ListBoxItemBackgroundBrushKey}}" />
<Setter Property="SelectionMode" Value="Single" />
<Setter Property="Height" Value="234" />
<Setter Property="ItemTemplateSelector">
<Setter.Value>
<sc:ProductListTemplateSelector>
<sc:ProductListTemplateSelector.NormalItemTemplate>
<DataTemplate>
<StackPanel RenderTransformOrigin="0.5, 0.5"
Margin="7,0,0,0"
MinWidth="171" MaxWidth="171"
MinHeight="235" MaxHeight="235">
<Image Margin="14,21,21,11" Source="{Binding XPath=#Image}"
Height="149" Width="101" />
<TextBlock Text="{Binding XPath=#Name}"
MaxWidth="116"
FontSize="12"
Margin="21,0,21,21"
FontFamily="Segoe360"
TextAlignment="Center"
TextWrapping="Wrap"
Foreground="{DynamicResource {x:Static s:SurfaceColors.ListBoxItemForegroundBrushKey}}"
HorizontalAlignment="Center" />
</StackPanel>
</DataTemplate>
</sc:ProductListTemplateSelector.NormalItemTemplate>
I need to replace the DataTemplate of this style to be set using my user control like
<local:MyUserControl>
By keeping only between section I did not get my control displayed when my Itemsource is set with a collection of myUserControl

Usually I just add the DataTemplate in the Resources. This can be <Window.Resources> or <App.Resources> if the data template is global, or FrameworkElement.Resources if the template should only be applied in a specified scope. For example, adding the template to ListView.Resources would only apply the template within a specific ListView.
<Window.Resources>
<DataTemplate DataType="{x:Type local:ProductModel}">
<local:MyUserControl />
</DataTemplate>
</Window.Resources>
As a side note, your original question leads me to believe that you are binding a ListView to a collection of MyUserControl objects. I really wouldn't recommend this, but if this is the case you can use a ContentControl in your DataTemplate with it's Content bound to your object, and it should display correctly.
<ContentControl Content="{Binding }" />

Related

WPF: Simple MSWord-like color picker button

Is there a quick and easy way to implement a color picker button like the one in MS Word?
I want the user to choose from a number of predefined colors in a dropdown. The selected color should be displayed when the button is closed. When the user clicks the button, an event or command should be triggered. I want to use one button instance to set the text color in a RichTextBox, and another one to set the background color (similar to MS Word).
My first approach was to use a ComboBox with a DataTemplate which contains a button.
This does not work: The button's command is not triggered when the user clicks it. Instead, the dropdown of the ComboBox opens. I don't know the exact reason, but maybe the IsHitTestVisible=false attributes in the ComboBox default template? See: http://msdn.microsoft.com/en-us/library/dd334408%28v=vs.95%29.aspx
<UserControl x:Class="MyBrushPicker"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
Height="Auto" Width="Auto" MinWidth="50">
<UserControl.Resources>
<!--
Provide two different DataTemplates:
- SelectionBoxTemplate is used to display the selected color when the ComboBox is closed.
It contains a button which should be clickable (does not work).
- NormalItemTemplate is used to display a color in the opened ComboBox popup.
See:
http://stackoverflow.com/questions/2214696/wpf-how-to-customize-selectionboxitem-in-combobox
-->
<DataTemplate x:Key="NormalItemTemplate">
<Border Height="44" Width="44" Margin="3,3,3,3" BorderThickness="1" BorderBrush="Black" Background="{Binding Mode=OneWay}"/>
</DataTemplate>
<DataTemplate x:Key="SelectionBoxTemplate">
<!-- This button cannot be clicked: PreviewMouseDown and Button_Click events are never triggered. -->
<Button Click="Button_Click" PreviewMouseDown="Button_PreviewMouseDown">
<Border Height="44" Width="44" Margin="3,3,3,3" BorderThickness="1" BorderBrush="Black" Background="{Binding Mode=OneWay}"/>
</Button>
</DataTemplate>
<DataTemplate x:Key="CombinedTemplate">
<ContentPresenter x:Name="Presenter"
Content="{Binding}"
ContentTemplate="{StaticResource NormalItemTemplate}" />
<DataTemplate.Triggers>
<DataTrigger
Binding="{Binding RelativeSource={RelativeSource FindAncestor,ComboBoxItem,1}}"
Value="{x:Null}">
<Setter TargetName="Presenter" Property="ContentTemplate"
Value="{StaticResource SelectionBoxTemplate}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</UserControl.Resources>
<ComboBox IsReadOnly="True" IsEditable="False"
ItemsSource="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectableBrushes, Mode=OneWay}"
SelectedItem="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=SelectedBrush, Mode=TwoWay}"
ScrollViewer.VerticalScrollBarVisibility="Auto"
MinHeight="50"
MaxDropDownHeight="200"
ItemTemplate="{StaticResource CombinedTemplate}"
>
<ComboBox.Resources>
<Style TargetType="ComboBox">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" Orientation="Horizontal" Width="200" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="ComboBoxItem">
<Setter Property="Width" Value="50" />
<Setter Property="Height" Value="50" />
</Style>
</ComboBox.Resources>
</ComboBox>
</UserControl>
Is there a way to make the button work correctly?
Are there any better approaches?
EDIT: Using the Snoop tool revealed that the IsHitTestVisible attribute of the unnamed ContentPresenter inside the ComboBox is actually set to false (ValueSource: ParentTemplate). If I set this property to true using Snoop, the button becomes clickable.
Can I change this property from a style?
At least, the following doesn't work:
<ComboBox.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="IsHitTestVisible" Value="True" />
</Style>
</ComboBox.Resources>
Assuming you are not just doing this as a learning exercise (which make using a library pointless)...
Take a look at: Extended WPF Toolkit - Color Picker
I've used it before for simple a plug-and-play WPF color-picker and it should solve your problem nicely.

Get control properties from Grid.Resources in code behind

At the begining, theres xaml code:
<Grid.Resources>
<DataTemplate x:Name="dataTemp" x:Key="dtKey">
<WrapPanel Orientation="Horizontal" Name="mainWP">
<TextBlock Name="codeTB" FontSize="18" Width="200" Text="{Binding barcode}"></TextBlock>
(...)
</WrapPanel>
</DataTemplate>
</Grid.Resources>
and listview with datatemplate:
<ListView Name="testLV" Grid.Row="0" ItemTemplate="{StaticResource ResourceKey=dtKey}" >
</ListView>
So in code behind i'd like to change TextBlock width as this.width/5 (becouse width could be different in another PC), but becouse it's DataTemplate i don't have access to this control.
I also tried Width="{Binding Path=ActualWidth, ElementName=grid0}", but as actual width i need something like ActualWidth/5, which doesnt work
Thanks
Using a Grid with 5 columns and each with Width="0.2*" will work fine when all the child element's DesiredWidth have been satisfied(In other words when the size of the Grid is large enough to fit all columns with equal space). If it cannot do this, The layout works in a way to trim elements it can and give the extra space to other columns that need it more thereby overriding the Width="0.2*" in the process
For your requirement where you want the 5 columns to be split equally, just use a UniformGrid. That pretty much doesn't care abt any of the above things.
So say something like:
<ListView Name="paragonLV" HorizontalContentAlignment="Stretch">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Padding"
Value="0" />
<Setter Property="BorderThickness"
Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<UniformGrid MaxWidth="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ScrollViewer}},
Path=ActualWidth}"
Columns="5">
<UniformGrid.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextTrimming"
Value="CharacterEllipsis" />
<Setter Property="FontSize"
Value="18" />
<Setter Property="HorizontalAlignment"
Value="Stretch" />
</Style>
</UniformGrid.Resources>
<TextBlock Text="{Binding barCode}" />
<TextBlock Text="{Binding nazwa}" />
<TextBlock Text="{Binding jm}" />
<TextBlock Text="{Binding ilosc}" />
<TextBlock Text="{Binding cena}" />
</UniformGrid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Use Grid.Columndefination to format your grid or otherwise use Ivaluconverter class let's see value converter development
convert parameter will your calculating parameter,
you know how to construct value converter class
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
//value is grid actual width
// parameter = 5 is your calculated value
return value / parameter;
}

WPF - MVVM - Conditionally include user controls to view

In WPF my MVVM application I need to create an account search view with 2 options simple search by account # or Advanced search (by name, email, etc.)
In my AccountSearchViewModel I have a bool property IsAdvancedMode.
Also I have created 2 UserControls for each mode: SimpleSearchView and AdvancedSearchView
Now I need to show either one based on IsAdvancedMode property.
What is the best way to do it?
Also as a general solution what if I have SearchMode property that is enum. How whould you switch between multiple controls in that case?
I think you need to use Data Templating, to do that you need to create three classes:
public class Search
{
//Your Code
}
public class AdvanceSearch : Search
{
//Your Code
}
public class SimpleSearch : Search
{
//Your Code
}
and then create Data Template base on Classes:
<DataTemplate DataType="{x:Type local:AdvanceSearch }">
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
<TextBlock Text="{Binding Path=Email}"/>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SimpleSearch }">
<StackPanel>
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
I would use a DataTrigger to swap out the ContentTemplate of a ContentControl as needed. I wrote an article about switching Views in MVVM here if you're interested (examples included)
Here's some quick test code demonstrating it:
<Window.Resources>
<DataTemplate x:Key="TemplateA" >
<TextBlock Text="I'm Template A" />
</DataTemplate>
<DataTemplate x:Key="TemplateB" >
<TextBlock Text="I'm Template B" />
</DataTemplate>
</Window.Resources>
<StackPanel>
<ToggleButton x:Name="Test" Content="Test" />
<ContentControl>
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource TemplateA}" />
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=Test, Path=IsChecked}" Value="True">
<Setter Property="ContentTemplate" Value="{StaticResource TemplateB}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</StackPanel>
I usually drop them both and then use the BooleanToVisibilityConverter. Simplest approach with what you've got set up.
<Grid>
<SimpleSearch />
<AdvancedSearch
Visibility="{Binding IsAdvancedMode, Converter={StaticResource btvc}"/>
</Grid>
When IsAdvancedMode is true, the AdvancedSearch control will overlay the SimpleSearch. Again, this is the simplest approach, not necessarily the absolute best.

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.

How can I use a custom TabItem control when databinding a TabControl in WPF?

I have a custom control that is derived from TabItem, and I want to databind that custom TabItem to a stock TabControl. I would rather avoid creating a new TabControl just for this rare case.
This is what I have and I'm not having any luck getting the correct control to be loaded. In this case I want to use my ClosableTabItem control instead of the stock TabItem control.
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True"
Controls:ClosableTabItem.TabClose="TabClosed" >
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type Controls:ClosableTabItem}" >
<TextBlock Text="{Binding Path=Id}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
EDIT: This is what I ended up with, rather than trying to bind a custom control.
The "CloseCommand" im getting from a previous question.
<Style TargetType="{x:Type TabItem}" BasedOn="{StaticResource {x:Type TabItem}}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border
Name="Border"
Background="LightGray"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="25,0,0,0"
SnapsToDevicePixels="True">
<StackPanel Orientation="Horizontal">
<ContentPresenter x:Name="ContentSite"
VerticalAlignment="Center"
HorizontalAlignment="Center"
ContentSource="Header"
Margin="20,1,5,1"/>
<Button
Command="{Binding Path=CloseCommand}"
Cursor="Hand"
DockPanel.Dock="Right"
Focusable="False"
Margin="1,1,5,1"
Background="Transparent"
BorderThickness="0">
<Image Source="/Russound.Windows;component/Resources/Delete.png" Height="10" />
</Button>
</StackPanel>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
<Setter TargetName="Border" Property="Background" Value="LightBlue" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
<Setter TargetName="Border" Property="BorderBrush" Value="DarkBlue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
found a way,
derive a class from TabControl and override this function, in my case I want the items of the tab control (when bound) to be CloseableTabItems
public class CloseableTabControl : TabControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new CloseableTabItem();
}
}
HTH Someone
Sam
You don't want to set the DataType of the DataTemplate in this case. The value of the ItemTemplate property is used whenever a new item needs to be added, and in the case of a tab control it will be used to create a new TabItem. You should declare an instance of your class within the DataTemplate itself:
<TabControl x:Name="tabCases" IsSynchronizedWithCurrentItem="True" Controls:ClosableTabItem.TabClose="TabClosed">
<TabControl.ItemTemplate>
<DataTemplate>
<Controls:ClosableTabItem>
<TextBlock Text="{Binding Path=Id}" />
</Controls:ClosableTabItem>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type Entities:Case}">
<CallLog:CaseReadOnlyDisplay DataContext="{Binding}" />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This will cause a new ClosableTabItem to be created whenever a new tab is added to the TabControl.
Update; From your comment, it sounds like that the ItemTemplate controls what is created within the TabItem, rather than changing the TabItem itself. To do what you want to do, but for a TreeView, you would set the HeaderTemplate. Unfortunately, I don't see a HeaderTemplate property of TabControl.
I did some searching, and this tutorial modifies the contents of the tab headers by adding controls to TabItem.Header. Maybe you could create a Style for your TabItems that would add the close button that your class is currently adding?

Resources