I have two comboboxes.
for FontFamily.
for FontWeight.
Xaml for 1st Combobox looks like:
<ComboBox IsEditable="True"
ItemsSource="{x:Static Fonts.SystemFontFamilies}" >
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type FontFamily}">
<TextBlock Text="{Binding}" FontFamily="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
In the output of the above XAML I can see all the font-names in its own style. I want to do something similar for 2nd combobox also. Currently I have some items in 2nd combobox as below:
<ComboBox IsEditable="True">
<x:Static Member="FontStyles.Normal"/>
<x:Static Member="FontStyles.Italic"/>
<x:Static Member="FontStyles.Oblique"/>
</ComboBox>
How can I show each item in above combobox in its own style using Combobox.ItemTemplate or something similar without styling each item.
For e.g. my output should look something like:
Normal
Italic
Oblique
Take advantage of type converters: for most properties there's a converter that converts a string to a suitable value for the property. It's needed to be able to parse XAML (which is all strings) to types (think of writing something like Width="Auto" while remembering that Width is a double value).
So, you can use something like this:
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding }"
FontStyle="{Binding }" />
</DataTemplate>
</ComboBox.ItemTemplate>
<system:String>Normal</system:String>
<system:String>Italic</system:String>
<system:String>Oblique</system:String>
</ComboBox>
The binding for FontStyle sets a string and then type converter springs into action and converts the string to an actual FontStyle value to be used by the property.
NOTE: this might not work in .NET 3.0/3.5
EDIT : just remembered, in .NET 3.0/3.5, if there's a converter defined for the binding, then type converter is not working - the binding expects the converter to return the right type for the property. Not sure if it was changed in .NET 4.0/4.5 (probably not, and ,IMHO, shouldn't have - need to check it to verify).
Oh, and add this xmlns: xmlns:system="clr-namespace:System;assembly=mscorlib"
Related
I have a Dictionary acting as sort of a phonebook. It uses a character from the alphabet as a key, and an observable collection of classes as the value. These classes include properties like FirstName, LastName, etc...
It looks like this
Dictionary<char,ObservableCollection<NameClass>> NameDictionary
The goal is to be able to use this dictionary as an itemsource for a listbox, and insert the key into the listbox, followed by every NameClass.FirstName that is in the observable collection for that specific key.
This is a stripped-down version DataTemplate I currently have
<DataTemplate x:Key="HeaderTemplate" >
<TextBlock Text="{Binding Key}"/>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
This is the ListBox that uses the DataTemplate and NameDictionary as a source.
<ListBox x:Name="NameList"
Style="{StaticResource ListStyle}"
ItemContainerStyle="{StaticResource ContainerStyle}"
ItemsPanel="{StaticResource PanelTemplate}"
ItemTemplate="{StaticResource HeaderTemplate}"
ItemsSource="{Binding NameDictionary, IsAsync=True}"
SelectedValue="{Binding ItemIndex}"
SelectedValuePath="Key">
The xaml I currently have will insert the key and ObservableCollection into the Listbox, but I want it to insert all of the items inside of the ObservableCollection, not the collection itself.
For a visual example, I want to make something that looks like this
Char
NameClass.FirstName
NameClass.FirstName
NameClass.FirstName
Char
NameClass.FirstName
NameClass.FirstName
NameClass.FirstName
But what is currently being inserted is this
Char
ObservableCollection
Char
ObservableCollection
Char
ObservableCollection
I would prefer to use xaml for this, and not code behind. I assume this is not too difficult to do, and I will appreciate any help with this.
Your "HeaderTemplate" does not compile because it lacks a panel which hosts multiple elements. Said that, you can modify it to host another ListBox whose ItemsSource to be bound to Value as follows.
<DataTemplate x:Key="HeaderTemplate">
<StackPanel>
<TextBlock Text="{Binding Key}"/>
<ListBox ItemsSource="{Binding Value}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding FirstName}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
I have a fairly dynamic ObservableCollection of view models that is used by two different ListBox elements in XAML. Each view model contains properties for two different model objects of type Card called Primary and Secondary, as well as other properties. In one ListBox I'd like to display properties from Primary and in the other I'd like to display properties from Secondary. I'd like to use the same XAML UserControl file when displaying the ListBoxItems for both.
My first thought was to create an entry in UserControl.Resources that gives a name to the "right" card based on a RelativeSource reference from the parent view model which indicates Primary or Secondary, but I've not created an entry like that before. Is this the right approach? If so, what would the entry look like?
I've made up some XAML to help illustrate (may have typos). First, the Primary ListBox control:
<UserControl x:Class="Project.Cards.ListPrimary" d:DataContext="{Binding Main.Cards.Primary, Source={StaticResource Locator}}">
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type vms:CardViewModel}">
<views:Card />
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<ListBox x:Name="CardListBox"
SelectedItem="{Binding SelectedCard}"
ItemsSource="{Binding Cards}" />
</Grid>
</UserControl>
And the secondary:
<UserControl x:Class="Project.Cards.ListSecondary" d:DataContext="{Binding Main.Cards.Secondary, Source={StaticResource Locator}}">
... (same) ...
</UserControl>
And the card view (where I need to replace "Primary.Direction" with something that lets me select Primary/Secondary):
<UserControl x:Class="Project.Cards.Card">
<UserControl.Resources>
... perhaps something here ...
</UserControl.Resources>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Description}" />
<TextBlock Text="{Binding Primary.Direction}" />
</StackPanel>
</UserControl>
If you want two instances of the same UserControl that differ in one respect, you figure out how to parameterize that. There are a couple of ways, but the simplest I thought of that fits your case was to just bind the differing value to a property of the View. This moves the specification of the different value to the owner.
We'll do that by defining a dependency property on the UserControl. It's a string, though it could be an object, and in the future you might want to make it one. Since we're using the view in a DataTemplate, we can bind a property of the DataContext to it there.
public partial class Card : UserControl
{
public Card()
{
InitializeComponent();
}
public String Direction
{
get { return (String)GetValue(DirectionProperty); }
set { SetValue(DirectionProperty, value); }
}
public static readonly DependencyProperty DirectionProperty = DependencyProperty.Register("Direction",
typeof(String), typeof(Card), new PropertyMetadata(null));
}
...and we'll use that in the UserControl like this:
<Grid>
<StackPanel>
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Description}" />
<TextBlock
Text="{Binding Direction, RelativeSource={RelativeSource AncestorType=UserControl}}"
/>
</StackPanel>
</Grid>
The RelativeSource stuff tells the Binding to look for that Direction property on the UserControl object itself, rather than on the DataContext as it would otherwise do by default.
If Card.Direction were object instead of string, you'd make that TextBox a ContentControl and bind to its Content property. Then you could put anything in there -- XAML, a whole other viewmodel, literally anything that XAML can figure out how to display.
And here's how it looks in the wild:
<DataTemplate DataType="{x:Type vms:CardViewModel}">
<views:Card Direction="{Binding Primary.Direction}" />
</DataTemplate>
And here's my whole mainwindow content from my test code. I didn't bother creating user controls for the listboxes; the above template is an exact match for the way you're doing it.
<Window.Resources>
<DataTemplate x:Key="PrimaryItemTemplate" DataType="{x:Type vms:CardViewModel}">
<views:Card Direction="{Binding Primary.Direction}" />
</DataTemplate>
<DataTemplate x:Key="SecondaryItemTemplate" DataType="{x:Type vms:CardViewModel}">
<views:Card Direction="{Binding Secondary.Direction}" />
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal">
<ListBox
SelectedItem="{Binding SelectedCard}"
ItemsSource="{Binding Cards}"
ItemTemplate="{StaticResource PrimaryItemTemplate}"
/>
<ListBox
SelectedItem="{Binding SelectedCard}"
ItemsSource="{Binding Cards}"
ItemTemplate="{StaticResource SecondaryItemTemplate}"
/>
</StackPanel>
</Grid>
I originally thought of a more elaborate scheme where you give the view a DataTemplate instead, and it worked, but this is simpler. On the other hand, that was more powerful. I actually used that in the first version of the answer, before I came to my senses; it's in the edit history.
Thanks for a fun little projectlet.
I have a collection that looks like this:
ObservableDictionary<string, ObservableDictionary<string, SystemStatusItem>> ObservableColoServerStatus
I am working on creating a user control that I would like to bind as follows:
Each key (string) key from the outer dictionary should dynamically create an expander.
Inside that expander is a ListView/DataGrid/some control for displaying a collection of information
Each line in the ListView/DataGrid or whatever will be composed of a combination of the (string) Key and a property from the (object)Value of the inner dictionary.
Ideally it should look and operate similarly to a TreeView however Expanders have been widely used through our UI so I would like to stick to this for consistency.
I am new to WPF and have done a bit of basic data binding before but the nature of this nested relationship is making this really difficult to wrap my head around
Ok, first lets asume that you ObservableDictionary is a collection of items that have a Key property and a Value property. What you want can be done in wpf using data templates, please see this code, try do it in this way:
<ListBox ItemsSource="{Binding ObservableColoServerStatus}">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding Key}">
<ListBox ItemsSource="{Binding Value}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Key}"/>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It is a list box, that has as ItemsSource your ObservableDictionary, then in the item template property you set the template that will use for representing all it itmes. In this case an expander, that have in the header the key a list box that take the elements from the Value of the first dictionary, and then you have set the template again. It is similar. Hope it works for you...
On my viewmodel I've got an int property and I want to expose it for editing with a ComboBox, with a limited set of choices, such as 16, 8, 4 and 2. Is there a way to specify the choices in the XAML, while still binding the value back to the viewmodel? I'd want to do something like this:
<ComboBox SelectedValue="{Binding MyIntProperty}">
<ComboBoxItem>16</ComboBoxItem>
<ComboBoxItem>8</ComboBoxItem>
<ComboBoxItem>4</ComboBoxItem>
<ComboBoxItem>2</ComboBoxItem>
</ComboBox>
I know I could rig up a List<int> in code and set that as the ItemsSource, but I'm hoping there's a way to do this that doesn't involve an extra property in the viewmodel that exposes a collection created in code.
You can specify your choices exactly as you are in your example. What it looks like your missing, to make it work, is the SelectedValuePath property. Without it, the SelectedValue would be the same as the SelectedItem. By setting SelectedValuePath="Content" in the ComboBox you can specify that your SelectedValue binding is instead binding to just a portion of the SelectedItem, in this case the Int content you specified as the content in each ComboBoxItem.
Here's a small demo with it, and also binding the value to a TextBox, where you can set the item and see it reflected in the ComboBox through the SelectedValue binding (or vice versa).
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Set Value:" />
<TextBox Text="{Binding MyIntProperty, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Select Value:" />
<ComboBox SelectedValue="{Binding MyIntProperty}" SelectedValuePath="Content">
<ComboBoxItem>2</ComboBoxItem>
<ComboBoxItem>4</ComboBoxItem>
<ComboBoxItem>6</ComboBoxItem>
<ComboBoxItem>8</ComboBoxItem>
<ComboBoxItem>16</ComboBoxItem>
</ComboBox>
</StackPanel>
</StackPanel>
I have a DataGrid whose ItemsSource is bound to a changing Observable Collection. Inside of this collection is a Business Object. Based on some of the values of the Business Object's properties, I would like to be able to modify the color of the text for each item displayed in my DataGrid once the ItemsSource is created.
Has anyone done this before or ran across something similar? Thanks in advance.
<DataTemplate x:Key="MyTemplate">
<Grid x:Name="LayoutRoot">
<TextBlock Text="{Binding MyText}"
Foreground="{Binding MyStatus, Converter={StaticResource colorConverter}}" />
</Grid>
</DataTemplate>
I added the above code and inserted the TemplateColumn to the grid as below:
<data:DataGridTemplateColumn Header="Testing"
CellTemplate="{StaticResource MyTemplate}"/>
The code works fine and pulls out the correct text but the converter never fires and the Binding of the foreground is never called from the get on it.
Any ideas?
Yes. Use a Value Converter when databinding.
<UserControl.Resources>
<myconverters:BackColor x:Key="BackColor" />
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="{Binding SomeValue, Converter={StaticResource BackColor}" >
</Grid>
Then have your converter class implement IValueConverter and return a Brush object of some kind. You usually don't have to implement ConvertBack()
Adding to BC's answer:
You can make a DataGridTemplateColumn and specify a data template for cells in a column. In the data template you can bind the text colour.
<swcd:DataGrid ... >
<swcd:DataGrid.Columns>
<swcd:DataGridTemplateColumn Header="MyColumn" CellTemplate="{StaticResource MyColumnDataGridCellTemplate}"/>
...
in resources:
<DataTemplate x:Key="MyColumnDataGridCellTemplate">
<Grid>
<TextBlock Text="{Binding someproperty}" Foreground="{Binding someotherproperty, Converter={StaticResource MyConverter}}"/>
...