WPF c# setting style with parameters - c#

I'm looking for a way to assign style with parameters(most of them just text) and assign to specified blocks
<StackPanel Orientation="Horizontal">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="field1" Text="Field1"/>
</StackPanel>
</StackPanel>
<StackPanel>
<Button BorderThickness="0">
<Button.Content>
<Border CornerRadius="18" BorderThickness="1" BorderBrush="#FFCFCFCF" >
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="10" Text="Default" Foreground="#FFCFCFCF" Margin="0" FontWeight="Black"/>
</StackPanel>
</Border>
</Button.Content>
</Button>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock x:Name="field2" Text="Field2"/>
</StackPanel>
</StackPanel>
<StackPanel>
<Button BorderThickness="0">
<Button.Content>
<Border CornerRadius="18" BorderThickness="1" BorderBrush="#FFCFCFCF" >
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="10" Text="RCC" Foreground="#FFCFCFCF" Margin="0" FontWeight="Black"/>
</StackPanel>
</Border>
</Button.Content>
</Button>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock x:Name="field3" Text="Field3"/>
</StackPanel>
</StackPanel>
<Rectangle Width="1" Fill="Black" Height="42" VerticalAlignment="Center"/>
<StackPanel Orientation="Horizontal">
<Button BorderThickness="0">
<Button.Content>
<Border CornerRadius="18" BorderThickness="1" BorderBrush="#FFCFCFCF" >
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="10" Text="Custom" Foreground="#FFCFCFCF" FontWeight="Black"/>
</StackPanel>
</Border>
</Button.Content>
</Button>
<TextBox Width="90" Height="15"/>
<Button BorderThickness="0">
<Button.Content>
<Border CornerRadius="18" BorderThickness="1" BorderBrush="#FFCFCFCF" >
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="10" Text="Apply" Foreground="#FFCFCFCF" FontWeight="Black"/>
</StackPanel>
</Border>
</Button.Content>
</Button>
</StackPanel>
</StackPanel>
There is 3 TextBlocks(field1,field2,field3), now is there any way to pass parameters(parameters are string type), to this template, and this template is generated through loop. And how to do it? Of course I could make everything in c# but thought it would be much easier just create field(stackpanel) and assign parameters
<stackpanel Style="{StaticResource mystyle}" param1="hello" param2="this" param3="world"/>
This would be perfect if its possible to make this way. Unless there is better one. Thanks for help.

You can by declaring your own Styles and Control templates with additional use of DependencyProperties.
A DependencyProperty is basically a declaration on your own custom class of your own custom property that you want to expose available during xaml entry and can also be applied to your style templates.
Once that is done, you then define your style, plenty of resources on that. Include your dependency properties as {TemplateBinding} to your custom properties.
Then add instance of your new class to your form, and specify which style to use. I have a sample showing utilization of TWO styles under the same class. I first started with a brand new WPF application. In the MainWindow.xaml.cs, I defined my own class based on a type of UserControl (which can then hold any other control(s) such as you have nested). I added 3 Dependency Properties to reflect 3 possible text values you want to implement.
public class MyControl : UserControl
{
public static readonly DependencyProperty MyText1Property =
DependencyProperty.Register("MyText1", typeof(string),
typeof(MyControl), new UIPropertyMetadata(""));
public string MyText1
{
get { return (string)GetValue(MyText1Property); }
set { SetValue(MyText1Property, value); }
}
public static readonly DependencyProperty MyText2Property =
DependencyProperty.Register("MyText2", typeof(string),
typeof(MyControl), new UIPropertyMetadata(""));
public string MyText2
{
get { return (string)GetValue(MyText2Property); }
set { SetValue(MyText2Property, value); }
}
public static readonly DependencyProperty MyText3Property =
DependencyProperty.Register("MyText3", typeof(string),
typeof(MyControl), new UIPropertyMetadata(""));
public string MyText3
{
get { return (string)GetValue(MyText3Property); }
set { SetValue(MyText3Property, value); }
}
}
Next, my application name is StackOverflow for sample purposes, and in the following is the entire MainWindow.xaml. Clarification of components follows code.
<Window.Resources>
<Style TargetType="{x:Type myApp:MyControl}" x:Key="MyControlStyle1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type myApp:MyControl}" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{TemplateBinding MyText1}"/>
<TextBlock Text="{TemplateBinding MyText2}"/>
<TextBlock Text="{TemplateBinding MyText3}"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type myApp:MyControl}" x:Key="MyControlStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type myApp:MyControl}" >
<StackPanel Orientation="Horizontal" Grid.Row="0">
<TextBlock Text="{TemplateBinding MyText1}"/>
<StackPanel>
<Button BorderThickness="0">
<Button.Content>
<Border CornerRadius="18" BorderThickness="1" BorderBrush="#FFCFCFCF" >
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="10" Text="Default" Foreground="#FFCFCFCF" Margin="0" FontWeight="Black"/>
</StackPanel>
</Border>
</Button.Content>
</Button>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="{TemplateBinding MyText2}"/>
</StackPanel>
</StackPanel>
<StackPanel>
<Button BorderThickness="0">
<Button.Content>
<Border CornerRadius="18" BorderThickness="1" BorderBrush="#FFCFCFCF" >
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="10" Text="RCC" Foreground="#FFCFCFCF" Margin="0" FontWeight="Black"/>
</StackPanel>
</Border>
</Button.Content>
</Button>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="{TemplateBinding MyText3}"/>
</StackPanel>
</StackPanel>
<Rectangle Width="1" Fill="Black" Height="42" VerticalAlignment="Center"/>
<StackPanel Orientation="Horizontal">
<Button BorderThickness="0">
<Button.Content>
<Border CornerRadius="18" BorderThickness="1" BorderBrush="#FFCFCFCF" >
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="10" Text="Custom" Foreground="#FFCFCFCF" FontWeight="Black"/>
</StackPanel>
</Border>
</Button.Content>
</Button>
<TextBox Width="90" Height="15"/>
<Button BorderThickness="0">
<Button.Content>
<Border CornerRadius="18" BorderThickness="1" BorderBrush="#FFCFCFCF" >
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="10" Text="Apply" Foreground="#FFCFCFCF" FontWeight="Black"/>
</StackPanel>
</Border>
</Button.Content>
</Button>
</StackPanel>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- NOW, we can expand the custom properties-->
<Style TargetType="{x:Type myApp:MyControl}" BasedOn="{StaticResource MyControlStyle}" />
</Window.Resources>
<Grid Height="150">
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<myApp:MyControl MyText1="First String" MyText2="Second String" MyText3="Third String"
Style="{StaticResource MyControlStyle}"/>
<myApp:MyControl MyText1="Another Line" MyText2="diff string" MyText3="all done" Grid.Row="1"/>
<myApp:MyControl MyText1="Another Line" MyText2="diff string" MyText3="all done" Grid.Row="2"
Style="{StaticResource MyControlStyle1}"/>
</Grid>
At the top within the main declaration, I added
xmlns:myApp="clr-namespace:StackOverflow"
this basically states that when within this xaml file, I see a prefix of "myApp", it is similar to a "using StackOverflow;" command as if in code. So now I have access to the custom class(es) or other things within that namespace to the xaml.
Next I start to declare my own "style" for the custom MyControl class via
<Window.Resources>
<Style TargetType="{x:Type myApp:MyControl}" x:Key="MyControlStyle1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type myApp:MyControl}" >
...
You might want to create a separate ResourceDictionary if you deal with many styles / templates used throughout your app. Notice the "Syle" and custom "ControlTemplate" are based on the "myApp:MyControl" class structure. Now, I can make use of my "MyText1", "MyText2", "MyText3" elements within the control template.
The x:Key="MyControlStyle1" is like creating a variable by given name so it can be used if you need to explicitly say which style to use. This first style is just to show the point that the 3 "MyText" properties are available and the Text is getting its value from the
Text="{TemplateBinding MyText1}"
Class that the control template is bound to (hence TemplateBinding).
Once you get the basics working, then you can glorify your template as you have with your nested stack panels which is the lower
<Style TargetType="{x:Type myApp:MyControl}" x:Key="MyControlStyle">
declaration by a different x:Key name.
Now, so you don't have to explicitly keep adding xaml for your control and say... by the way, use this explicit style of MyControlStyle, I have the following
<Style TargetType="{x:Type myApp:MyControl}" BasedOn="{StaticResource MyControlStyle}" />
indicating whenever you see a target type of "MyControl", default the style to the "MyControlStyle" so I don't have to keep remembering to do it.
Finally implementing its use. The end of the code has a simple Grid control with 3 rows.
<myApp:MyControl MyText1="First String" MyText2="Second String" MyText3="Third String"
Style="{StaticResource MyControlStyle}"/>
<myApp:MyControl MyText1="Another Line" MyText2="diff string" MyText3="all done" Grid.Row="1"/>
<myApp:MyControl MyText1="Another Line" MyText2="diff string" MyText3="all done" Grid.Row="2"
Style="{StaticResource MyControlStyle1}"/>
Notice the first instance I CAN explicitly declare the style to be used. The second has no explicit style as per the default, but the third instance explicitly states to use the simplified "MyControlStyle1" which was just the 3 textblocks side-by-side showing that you can have one class and make it look differently as needed.
Revision per questions/comments.
If you are building these controls based on a loop and dynamically adding them, you would just set the properties respectively in the code. Performance should not be significant because the CLASS is already declared, you are just adding one more into your list.
foreach( var oneThing in YourListOfToBeAddedItems )
{
var mc = new MyControl();
mc.MyText1 = oneThing.TextFieldUsedForField1;
mc.MyText2 = oneThing.FieldForSecondText;
mc.MyText3 = oneThing.ThirdTextBasisForDisplay;
// Now, add the "mc" to whatever your control is
// can't confirm this line below as I dont know context
// of your form and dynamic adding.
YourWindowGridOrOtherControl.Controls.Add( mc );
}
Also, since the default style was defined, I would not need to explicitly declare the "Style" for the control.

Related

Listview items based on datatypes with delete buttons

I have something like a regex search pattern and this pattern consists of objects of type Variable or Literal. I need to show those objects one after another in line like this:
Variable is blue, Literal is red. I have defined two DataTemplates to set the colors.
<DataTemplate DataType="{x:Type local:Literal}">
<Border BorderThickness="1" BorderBrush="Red" Padding="5" Margin="0 0 1 0"
Background="Black">
<TextBox Text="{Binding Text}" />
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Variable}">
<Border BorderThickness="1" BorderBrush="Blue" Padding="5" Margin="0 0 1 0"
Background="Black">
<TextBox Text="{Binding Text}" />
</Border>
</DataTemplate>
My idea was to put a collection of these objects into a ListView and add delete buttons somehow so I can delete objects from the collection, but I am not sure how do that.
<ListView ItemsSource="{Binding RegExList}" Margin="5" Grid.Column="0"
HorizontalAlignment="Left" VerticalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListView>
RegExList is where those objects are.
You could add commands to the view model that contains the RegExList to delete items.
There are different implementations of ICommand. If you do not have a concrete command type, you could take it e.g. from here. The RelayCommand takes a method to execute and a method that returns whether the command can be executed with the given parameter. If you are new to commands, you can have a look at this article.
public class MyViewModel : INotifyPropertyChanged
{
public ICommand DeleteVariable { get; }
public ICommand DeleteLiteral { get; }
// ...your RegExList collection, other properties.
public MyViewModel()
{
DeleteVariable = new RelayCommand<Variable>(ExecuteDeleteVariable, CanExecuteDeleteVariable);
DeleteLiteral = new RelayCommand<Literal>(ExecuteDeleteLiteral, CanExecuteDeleteLiteral);
}
private void CanExecuteDeleteVariable(Variable variable)
{
// Optionally add conditions on when deletion is allowed
return true;
}
private void ExecuteDeleteVariable(Variable variable)
{
RegExList.Remove(variable);
}
private void CanExecuteDeleteLiteral(Literal literal)
{
// Optionally add conditions on when deletion is allowed
return true;
}
private void ExecuteDeleteLiteral(Literal literal)
{
RegExList.Remove(literal);
}
}
Bind a button in the DataTemplate to the corresponding command of the parent data context and bind the current item as CommandParameter.
<DataTemplate DataType="{x:Type local:Literal}">
<Border BorderThickness="1" BorderBrush="Red" Padding="5" Margin="0 0 1 0"
Background="Black">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Text}" />
<Button Content="X"
Command="{Binding DataContext.DeleteVariable, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
CommandParameter="{Binding}"/>
</StackPanel>
</Border>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Variable}">
<Border BorderThickness="1" BorderBrush="Blue" Padding="5" Margin="0 0 1 0"
Background="Black">
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Text}" />
<Button Content="X"
Command="{Binding DataContext.DeleteLiteral, RelativeSource={RelativeSource AncestorType={x:Type ListView}}}"
CommandParameter="{Binding}"/>
</StackPanel>
</Border>
</DataTemplate>
As a note, your RegExList collection should be an ObservableCollection<T>, otherwise removing items from the collection will not be reflected in your ListView.

How do I add more options in a generic Combo Box

I want to have a combobox like this:
But in code there is no way of doing it. I'm using MVVM pattern. So I have a view:
<ComboBox Style="{StaticResource ComboStyle}" ItemsSource="{Binding ResultObjects}" SelectedItem="{Binding SelectedObject,Mode=TwoWay}" />
and ViewModel:
public IEnumerable<DateTime> ResultObjects { get;set; }
public DateTime SelectedObject{ get;set; }
The fact is that the -All- and -custom- are not DateTime. And it can't be in added to this list.
I remember that in MVC we had a "Dropdown helper".
What can I do here in MVVM?
You could bind to an IEnumerable<KeyValuePair<DateTime?, string>> where the key represent the actual value and the value represents a custom string representation of that value:
public IEnumerable<KeyValuePair<DateTime?, string>> ResultObjects { get; set; }
public DateTime? SelectedObject { get; set; }
...
ResultObjects = new List<KeyValuePair<DateTime?, string>>()
{
new KeyValuePair<DateTime?, string>(null, "All"),
new KeyValuePair<DateTime?, string>(new DateTime(2018,04,17), new DateTime(2018,04,17).ToString("yyyy/MM/dd")),
new KeyValuePair<DateTime?, string>(new DateTime(2018,04,17), new DateTime(2018,04,17).ToString("yyyy/MM/dd # HH:mm:ss")),
new KeyValuePair<DateTime?, string>(new DateTime(2018,04,17), "Custom...")
};
...
XAML:
<ComboBox
ItemsSource="{Binding ResultObjects}"
SelectedValue="{Binding SelectedObject}"
DisplayMemberPath="Value"
SelectedValuePath="Key"/>
Obviosuly you cannot return anything but actual DateTime values from an IEnumerable<DateTime> to you should change the type of your source collection if you want to be able to represent other types of values as well.
The way I'd handle this is to define an ObservableCollection<object>.
When wpf comes across an object presented to the ui it first looks to see if it's got a template defined for the thing. If it hasn't, it will use ToString on the object. You can rely on that for simple cases and override .ToString() on any object you want to use.
If you want more sophisticated display than just a string then you can define a datatemplate which targets your objects based on their datatype.
One trick which can be handy.
You can even inherit from one base object and define a template for that, then more specific ones for sub types. A sub type inheriting from your base object will be dealt with by your "default".
Eg.
I have a map editor. The user is selecting from different terrains he's going to draw. I want to display different stuff for these. I have a BaseTerrainVM and then I inherit from that for river, contour, woods etc.
Here's a subset of the markup I use to template the items in a listbox:
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:BaseTerrainVM}">
<Grid>
<TextBlock Text="{Binding DisplayType}" HorizontalAlignment="Left"
VerticalAlignment="Center"/>
<TextBlock Text="{Binding ID}"
HorizontalAlignment="Right"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:ContourVM}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DisplayType}"
Grid.Column="0"
VerticalAlignment="Center"/>
<StackPanel Orientation="Horizontal"
Grid.Column="1"
TextElement.FontFamily="Century Gothic"
TextElement.FontSize="{DynamicResource LargeFont}"
TextElement.FontWeight="Normal"
>
<TextBox MinWidth="50"
Text="{Binding Height}"
GotKeyboardFocus="TextBox_GotKeyboardFocus"
>
<i:Interaction.Behaviors>
<ui:TextBoxDecimalRangeBehaviour MaxDecimals="0"
MaxInteger="3"
Minimum="{StaticResource Zero}"
Maximum="{StaticResource TwoFiveFive}" />
<ui:SelectAllTextBoxBehavior/>
</i:Interaction.Behaviors>
</TextBox>
<TextBlock Text="units"
Margin="2,0,0,0"
ToolTip="Elevation is represented by a number 0-255 which is multiplied by a factor to give metres"
/>
</StackPanel>
<TextBlock Text="{Binding ID}"
Grid.Column="2"
VerticalAlignment="Center"
/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:RiverVM}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding DisplayType}"
Grid.Column="0"
VerticalAlignment="Center"/>
<StackPanel Orientation="Horizontal"
Grid.Column="1"
TextElement.FontFamily="Century Gothic"
TextElement.FontSize="{DynamicResource LargeFont}"
TextElement.FontWeight="Normal"
>
<Button
Command="{Binding PressureFromStartCommand}"
Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Height="20"
ToolTip="Widen from Start of stroke"
>
<Path Data="{StaticResource FlowRight}"
Stretch="Uniform"
Fill="LightBlue"
Stroke="DodgerBlue"
StrokeThickness="1"
/>
</Button>
<Button
Command="{Binding PressureConstantCommand}"
Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Height="20"
ToolTip="Constant width for river"
>
<Rectangle
Width="18"
Height="6"
Fill="LightBlue"
Stroke="DodgerBlue"
StrokeThickness="1"
/>
</Button>
<Button
Command="{Binding PressureFromEndCommand}"
Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}"
Height="20"
ToolTip="Widen from End of stroke"
>
<Path Data="{StaticResource FlowRight}"
Stretch="Uniform"
Fill="LightBlue"
Stroke="DodgerBlue"
StrokeThickness="1"
RenderTransformOrigin="0.5,0.5"
>
<Path.RenderTransform>
<ScaleTransform ScaleX="-1" ScaleY="1" />
</Path.RenderTransform>
</Path>
</Button>
</StackPanel>
<TextBlock Text="{Binding ID}"
Grid.Column="2"
VerticalAlignment="Center"
/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type local:BoundaryVM}">
<Grid>
( This may amuse Muds. )
My Terrains that presents these is actually a composite collection. In my case this is because I translate all the water on the map into one object so there's no border between one road and the next or a lake and the river flowing into it. I need to present the two representations of water but switch between them.
You need to use CompositeCollection, that can handle more than one collection/s and other objects.
Try something like this
<ComboBox>
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem Content="--All--" />
<CollectionContainer Collection="{Binding Source=ResultObjects}" />
<ComboBoxItem Content="--Custom--" />
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>

listbox checkbox not binding to its source

I need help binding my checkbox list in listbox which is in a popup box. My XAML code is as follows
<Popup Name="popUser" Placement="Bottom" PlacementTarget="{Binding ElementName=BtnUserFilter}" StaysOpen="False" Height="100" VerticalAlignment="Top">
<Border Background="White" BorderBrush="Gray" BorderThickness="1,1,1,1">
<StackPanel Margin="5,5,5,15">
<StackPanel Orientation="Horizontal" Margin="0,0,0,20">
<Button Margin="0,0,0,0" Name="BtnSelectAll" Click="BtnSelectAll_Click">
<Button.Template>
<ControlTemplate>
<TextBlock Text="Select All" Foreground="Blue" Cursor="Hand" />
</ControlTemplate>
</Button.Template>
</Button>
<Button Margin="10,0,0,0" Name="BtnUnselectAll" Click="BtnUnselectAll_Click">
<Button.Template>
<ControlTemplate>
<TextBlock Text="Select None" Foreground="Blue" Cursor="Hand" />
</ControlTemplate>
</Button.Template>
</Button>
</StackPanel>
<ListBox x:Name="lstUser" BorderThickness="0" Height="100" ItemsSource="{Binding Userlists, RelativeSource={RelativeSource AncestorType=local:MainWindow}}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsChecked, UpdateSourceTrigger=PropertyChanged}"
Content="{Binding Path=UserIL, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Border>
</Popup>
The code in viewmodel to which the listbox is bound to is
Dim test = (From c In Users Select New With {Key c.User}).Distinct
Dim Cnt As Integer
For Cnt = 0 To test.Count - 1
UserLists.Add(New UserList(Of String) With {.IsChecked = True, .UserIL = test(Cnt).User})
Next
The code populates all the users in the UserList Variable but doesn't show up in the ListBox. Am I not binding the checkbox properly? Ideas?
Make sure that UserLists is an ObservableCollection because a regular list does not propagate changes made to its content to the UI.
Furthermore I would suspect you have a typo in your xaml by refering to the UserLists as Userlists (note case sensitivity)
For purposes of resolving backing types, WPF XAML is case sensitive by
the same rules that the CLR is case sensitive. Object elements,
property elements, and attribute names must all be specified by using
the sensitive casing when compared by name to the underlying type in
the assembly, or to a member of a type.

Creating collections of arbitrarily positioned controls in WPF

I have a simple Border and Grid that is positioned within a Canvas container in WPF. The position of the control changes during runtime hence why it resides in the canvas.
The XAML for the control looks something like this:
<Border Name="PopupArea"
Width="130"
Height="150"
BorderBrush="Black"
BorderThickness="2"
CornerRadius="5">
<Border.Background>
<SolidColorBrush Opacity="0.5" Color="Black" />
</Border.Background>
<Grid>
<StackPanel>
<Border HorizontalAlignment="Center">
<Border.Effect>
<DropShadowEffect />
</Border.Effect>
<TextBlock FontWeight="Bold" Style="{StaticResource SmallWhiteFont}">HELLO WORLD</TextBlock>
</Border>
</StackPanel>
</Grid>
</Border>
However I now need to be able to create a Collection of the above control which I create and destory as required at runtime in code.
My questions are :
A. What is the best way to compose my XAML to create multiple instances of the above control through code? Do I just declare a ContentControl in the resources?
B. Assuming I am correct and a resource template is required. How do I actually use it to create multiple instances of the control in code?
Within XAML I would use an ItemTemplate.
Then I would bind ItemsSource to some observable collection on my viewmodel.
Here's an example taken from MSDN:
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=TaskName}" />
<TextBlock Text="{Binding Path=Description}"/>
<TextBlock Text="{Binding Path=Priority}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
NOTE:
It is more common to define a DataTemplate in the resources section so it can be a reusable object.
<ListBox Width="400" Margin="10"
ItemsSource="{Binding Source={StaticResource myTodoList}}"
ItemTemplate="{StaticResource myTaskTemplate}"/>

WPF How to get the control in a nested template

So I've seen lots and lots of examples where someone has a nifty data template with a control in it and their content control is applying the template and their code behind needs to grab it.
But what I have is this:
<DataTemplate x:Key="frontTemplate" >
<StackPanel x:Name="noWork">
<Image Source="Images/1.png" Stretch="Fill" Width="72" Height="96" x:Name="FrontFace" HorizontalAlignment="Left" VerticalAlignment="Top"></Image>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="flipItemTemplate">
<Grid Width="200" Height="200">
<Border x:Name="frontHost" Background="Transparent">
<ContentPresenter Content="{Binding}" ContentTemplate="{StaticResource frontTemplate}" />
</Border>
</Grid>
</DataTemplate>
You can see that I have a data Template (frontTemplate) nested inside of another data template (flipItemTemplate). What I need to do is get access to the Stackpanel in frontTemplate. All of my attempts to get to the content presenter for that datatemplate have failed. I am hoping that the wise sages of StackOverflow can help me. How in god's name would I get to that panel????
Thanks!!
If your XAML is defined like this:
<Grid Name="mainGrid">
<Grid.Resources>
<DataTemplate x:Key="frontTemplate" >
<StackPanel x:Name="noWork">
<Image Source="Images/1.png" Stretch="Fill" Width="72" Height="96" x:Name="FrontFace" HorizontalAlignment="Left" VerticalAlignment="Top"></Image>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="flipItemTemplate">
<Grid Width="200" Height="200">
<Border x:Name="frontHost" Background="Transparent">
<ContentPresenter Name="contentPresenter" Content="{Binding}" ContentTemplate="{StaticResource frontTemplate}" />
</Border>
</Grid>
</DataTemplate>
</Grid.Resources>
</Grid>
You can use:
var template = mainGrid.FindResource("frontTemplate") as DataTemplate;
var stackPanel = template.LoadContent() as StackPanel;

Categories