I'm very new to WPF, and am just getting started with data binding. What I'd like to do is generate a list of checkboxes based on a list in my view model. The XAML I have at the moment is:
<ItemsControl ItemsSource="{Binding Path=TestList, UpdateSourceTrigger=PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=Name}" IsChecked="{Binding Path=Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="10,5,10,5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This works correctly, and generates a checkbox for every item in TestList. What I'd like to do is only generate checkboxes for items where the condition TestList[i].Type == "Mode" is true. I believe that I may need to use a <DataTrigger> element, but I don't know the details of how to do this.
[EDIT] Just to clarify, each element of TestList has Name, Enabled, and Type properties.
There are several ways to do this. However, the simplest static approach would be to just filter it at your ViewModel
Filtered = new ObservableCollection(TestList.Where(x => x.Type == "Mode"));
...
<ItemsControl ItemsSource="{Binding Path=Filtered , UpdateSourceTrigger=PropertyChanged}">
Note : There are fancier more dynamic ways to achieve this, though this might help you out
As I guess that you want to make the checkbox appear if TestList.Type changes, I would suggest make a Converter and Bind it to the CheckBox Visibility.
public sealed class CheckBoxVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || parameter == null)
return Visibility.Visible;
var type = (string)value;
var condition = (string)parameter;
return type.Equals(condition) ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
And then in the dictionary add the reference to your namespace
xmlns:converters="clr-namespace:Projct.Converters;
and in the resources dictionary
<converters:CheckBoxVisibilityConverter x:Key="CheckBoxConverter"/>
Finally in the xaml
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox
Margin="10,5,10,5"
Content="{Binding Path=Name}"
IsChecked="{Binding Path=Enabled, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding Path=Type, Converter={StaticResource CheckBoxConverter}, ConverterParameter=Mode}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
Related
My code looks something like this:
<DockPanel>
<Expander DockPanel.Dock="Top" Name="Expander1">
<local:ListView1 DataContext="{Binding Source1}"/>
</Expander>
<Expander DockPanel.Dock="Top" Name="Expander2">
<local:ListView1 DataContext="{Binding Source2}"/>
</Expander>
<Expander DockPanel.Dock="Top" Name="Expander3">
<local:ListView1 DataContext="{Binding Source3}"/>
</Expander>
</DockPanel>
ListView1 is just a user control that contains a ListView
I have the behavior set: when one Expander is open, all the other 2 Expanders will close.
The problem is that when Expander1 is open and its content is more than the window height, it will have a scroll bar to scroll down for its content while Expander2 and Expander3 are not displayed. I think Expander1 uses all the space on the UI and Expander2 & Expander3 get pushed out side of the UI. When Expander2 is open, Expander3 is pushed out of the UI and not displayed. What can I do so that when I open an Expander, the one(s) below it won't get pushed out of the UI?
You said you're only allowing one open at a time.
Given that then the size logic is simpler and you could use just a converter with a fixed number.
My PoC:
<DockPanel>
<Expander DockPanel.Dock="Top" MaxHeight="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=ContentPresenter},
Converter={local:AddConverter ValueToAdd=-46}}">
<ListBox ItemsSource="{Binding Items}"/>
</Expander>
<Expander DockPanel.Dock="Top" MaxHeight="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=ContentPresenter},
Converter={local:AddConverter ValueToAdd=-46}}">
<ListBox ItemsSource="{Binding Items}"/>
</Expander>
<Expander DockPanel.Dock="Top" MaxHeight="{Binding ActualHeight,
RelativeSource={RelativeSource AncestorType=ContentPresenter},
Converter={local:AddConverter ValueToAdd=-46}}">
<ListBox ItemsSource="{Binding Items}"/>
</Expander>
</DockPanel>
And the converter
public class AddConverter : MarkupExtension, IValueConverter
{
public double ValueToAdd { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double bound = (Double)value;
return bound + ValueToAdd;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
As I mentioned in the comments.
You could make this more sophisticated with a multibinding and multiconverter. That could allow for height when 2 are open.
I have a certain amount of rows in my ViewModel observablecollection that are being grouped and displayed in a datagrid.
<CollectionViewSource x:Key="ColViewSource" Source="{Binding Collection}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Cat"/>
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
My rows contain a textfield and a threestate checkbox that needs to be true or indeterminate state for the row to be considered "filled" (default false). I would like to display the amount of "filled" rows in my groupheader out of total items in group. For example if I have a group with 5 items and the user ticked 2 of the checkboxes I would like to see 2 out of 5 or 2 / 5 in group header. I can manage to get the name and itemcount without a problem to display in groupheader but Im stuck on displaying the amount of filled rows. Here is how I would like my group header to look.
<Expander.Header>
<StackPanel Orientation="Horizontal" Height="50">
<TextBlock Text="{Binding Name}"/>
implement this-->
<TextBlock Text = amount of items currently filled.
<TextBlock Text="{Binding ItemCount}"/>
</StackPanel>
</Expander.Header>
I have implemented propertychanged for both my collection and items. Im guessing it would require some sort of converter , can someone point me in the right direction?
According to this article, Your group style must look like this in Xaml:
<local:PercentageConverterx:Key="percentageConverter" />
//...
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Converter={StaticResource percentageConverter} }"/>
<TextBlock Text="{Binding ItemCount}"/>
</StackPanel>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</DataGrid.GroupStyle>
in which
public class PercentageConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
CollectionViewGroup cvg = value as CollectionViewGroup;
int count = 0;
int check = 0;
foreach (Item t in cvg.Items)
{
count++;
if (t.IsCheck== true)
check++;
}
return (check / (double)count).ToString("0.00") + "%";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return Binding.DoNothing;
}
}
Note that I assumed CollectionViewSource.Source property is set to a ObservableCollection in which Item is like this:
public class Item
{
public string Name {get; set; }
public bool? IsChecked { get; set; }
}
which is bound to the threestate CheckBox.
I have a RadDataBoundListBox (from Telerik) that represents the items of a List. Each item is separated by a bottom line x:Name="ItemSeparatorBorder". The ListBox itself has a header and a footer containing a line too (x:Name="ListTopBorder"and x:Name="ListBottomBorder"). Now I need a way to disable the line (x:Name="ItemSeparatorBorder") of the last item in this ListBox.
I thought about some Visibility binding to x:Name="ItemSeparatorBorder" with a Converter that matches the index of the current item to the total count of the ListBox. But I don't know how to implement it and I can't find any good sample.
The code should work on Windows Phone 8.0 / .NET 4.0.
This is my code so far:
<telerikPrimitives:RadDataBoundListBox
x:Name="ListBox"
ItemsSource="{Binding Items}">
<telerikPrimitives:RadDataBoundListBox.ListHeaderTemplate>
<DataTemplate>
<Grid Height="30">
<Border
x:Name="ListTopBorder"
Height="1"
VerticalAlignment="Bottom"
Background="Blue"/>
</Grid>
</DataTemplate>
</telerikPrimitives:RadDataBoundListBox.ListHeaderTemplate>
<telerikPrimitives:RadDataBoundListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="1"></RowDefinition>
</Grid.RowDefinitions>
<controls:ListItem Margin="30,10,0,10">/>
<Border
x:Name="ItemSeparatorBorder"
Grid.Row="1"
Height="1"
Background="Blue"
Margin="30,0,0,0"/>
</Grid>
</DataTemplate>
</telerikPrimitives:RadDataBoundListBox.ItemTemplate>
<telerikPrimitives:RadDataBoundListBox.ListFooterTemplate>
<DataTemplate>
<Grid Height="30">
<Border
x:Name="ListBottomBorder"
Height="1"
VerticalAlignment="Top"
Background="Blue"/>
</Grid>
</DataTemplate>
</telerikPrimitives:RadDataBoundListBox.ListFooterTemplate>
</telerikPrimitives:RadDataBoundListBox>
How can I hide the Border of the last item?
To make it more clear, I want to remove the last blue line here:
I think the best approach is to use an IValueConverter that gets a reference to the items collection. Make a converter that inherits DependencyObject, and give it a property for the source list. Then you can check the current item index:
public class ItemToVisibilityConverter : DependencyObject, IValueConverter
{
public IList Items
{
get { return (IList )GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty=
DependencyProperty.Register("Items", typeof(IList), typeof(ItemToVisibilityConverter), new PropertyMetadata(null));
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool hide = Items != null
&& value != null
&& Items.IndexOf(value) == Items.Count - 1;
return (hide ? Visibility.Collapsed : Visibility.Visible);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException;
}
}
The put a converter instance in the control's resources:
<telerikPrimitives:RadDataBoundListBox
x:Name="ListBox"
ItemsSource="{Binding Items}">
<telerikPrimitives:RadDataBoundListBox.Resources>
<ResourceDictionary>
<local:ItemToVisibilityConverter x:Key="ItemToVisibilityConverter"
Items="{Binding Items}" />
</ResourceDictionary>
</telerikPrimitives:RadDataBoundListBox.Resources>
...
</telerikPrimitives:RadDataBoundListBox>
Finally, in the "ItemTemplate", bind to the current item using the converter:
<Border x:Name="ItemSeparatorBorder"
Visibility="{Binding Converter={StaticResource ItemToVisibilityConverter}}"
... />
I have a data template with a TexBlock in XAML. This TexBlock shows a word in a word list. Every word I want to put the first letter capitalized, because all words are in lowercase.
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="AddrBookItemTemplate">
<StackPanel VerticalAlignment="Top">
<TextBlock Margin="5,0,0,0" FontSize="20" Text="{Binding name}" />
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
In c# implement the converter
namespace Converter.ViewModels
{
public class ToCapitalizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return char.ToUpper(value.ToString()[0]) + value.ToString().Substring(1);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return (value as string).ToLower();
}
}
}
In App.xaml
...
xmlns:vm="clr-namespace:Converter.ViewModels"
<Application.Resources>
<vm:ToCapitalizeConverter x:Key="ToCapitalizeConverter"/>
</Application>
In MainPage.xaml
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="AddrBookItemTemplate">
<StackPanel VerticalAlignment="Top">
<TextBlock Margin="5,0,0,0" FontSize="20" Text="{Binding name, Converter={StaticResource ToCapitalizeConverter}}" />
</StackPanel>
</DataTemplate>
</phone:PhoneApplicationPage.Resources>
You can use a converter as follows:
<TextBlock Margin="5,0,0,0" FontSize="20" Text="{Binding name, Converter ={StaticResource myConverter}}" />
Specific information on how to implement a converter can be found here. You can essentially perform any operation you like on the text. I actually like Humanizer to do these type of text conversions.
I have a ObservableCollection<TimeSpan> Laps which I am databinding to a gridview. This works as expected but I need to apply a converter to set the format of the TimeSpan:
In my resources:
<utils:TimeToStringConverter x:Key="myConverter"/>
My Gridview:
<GridView HorizontalAlignment="Left" Height="278" Margin="78,220,0,0" VerticalAlignment="Top" Width="1278" ItemsSource="{Binding model.Laps}" />
I have the following converter which I want to apply on the items of a GridView / ListView in Winrt:
public class TimeToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
TimeSpan t = (TimeSpan) value;
return t.ToString(#"hh\:dd\:ss\.fff");
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
I can't figure out how to get the converter to work, and when I apply it on the GridView then it is looking for me to convert an Observable collection rather than just a TimeSpan item. What should I do here ?
Regards
You need something like a
<GridView
...>
<GridView.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Converter={StaticResource myConverter}}" />
</DataTemplate>
</GridView.ItemTemplate>
Use the below modified line
I've just modified the item source like below
ItemsSource="{Binding model.Laps,Converter={StaticResource myConverter}}"
<GridView HorizontalAlignment="Left" Height="278" Margin="78,220,0,0" VerticalAlignment="Top" Width="1278" ItemsSource="{Binding model.Laps,Converter={StaticResource myConverter}}" />