Bind content presenter content to a visual element - c#

I have an items control with items source, and i want it to display previews
<ItemsControl ItemsSource="{Binding Path=Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl>
<ContentControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Preview}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Preview is a grid
public class Child
{
public Grid Preview { get; set; }
public Child()
{
Preview = new Grid();
Preview.Children.Add(new TextBlock() { Text = "Test"});
Preview.Background = new SolidColorBrush(Colors.Red);
}
}
But it doesn't seem to render anything, what am I missing?

try like this,
<ItemsControl ItemsSource="{Binding Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Preview}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
or
<ItemsControl ItemsSource="{Binding Path=Children}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentControl Content="{Binding}">
<ContentControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Preview}"/>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
there is no datacontext, so it didnt display anything.

Related

WPF Window to edit the user.settings. Why values are not changing?

I'm working on a wpf window to edit the user settings.
This is what I did so far:
<ListView Grid.Row="1"
ItemsSource="{Binding Source={x:Static properties:Settings.Default}, Path=PropertyValues}"
HorizontalContentAlignment="Stretch" Background="LightGray"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel HorizontalAlignment="Stretch"
IsEnabled="{Binding DataContext.Enabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<Label Width="200" Content="{Binding Name}"/>
<Label Width="200" Content="{Binding Path=Property.PropertyType}" Foreground="Gray" FontStyle="Italic"/>
<ContentControl VerticalContentAlignment="Center" Content="{Binding Path=PropertyValue}">
<ContentControl.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:String}">
<TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:Int32}">
<TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</ResourceDictionary>
</ContentControl.Resources>
</ContentControl>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The values are showing but they're not updated in Properties.Settings.Default when I change and save them with Properties.Settings.Default.Save();.
Is the two way binding correct?
Thanks
As suggested in the comments and in this answer, I need to encapsulate the System.Configuration.SettingsPropertyValue into a ViewModel that implements INotifyPropertyChanged. Otherwise the binding won't work.
ViewModel:
public class SettingsPropertyValueProxy : INotifyPropertyChanged
{
public string Name { get; }
public Type PropertyType => PropertyValue.GetType();
public object PropertyValue
{
get
{
return Properties.Settings.Default[Name];
}
set
{
try
{
Properties.Settings.Default[Name] = Convert.ChangeType(value, PropertyType);
Properties.Settings.Default.Save();
}
catch
{ }
}
}
public SettingsPropertyValueProxy(string name)
{
Name = name;
Properties.Settings.Default.PropertyChanged += (sender, e) => _OnPropertyChanged(e.PropertyName);
}
private void _OnPropertyChanged(string propertyName)
{
if (propertyName == Name) PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PropertyValue)));
}
public event PropertyChangedEventHandler PropertyChanged;
}
New Property to bind:
public IEnumerable<SettingsPropertyValueProxy> Values { get; }
= Properties.Settings.Default.Properties
.Cast<SettingsProperty>()
.Select(p => new SettingsPropertyValueProxy(p.Name))
.OrderBy(p => p.Name)
.ToArray();
Correct View and Correct DataTemplates:
<ListView Grid.Row="1"
ItemsSource="{Binding Path=Values}"
HorizontalContentAlignment="Stretch" Background="LightGray"
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel HorizontalAlignment="Stretch"
IsEnabled="{Binding DataContext.Enabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}">
<Label Width="200" Content="{Binding Path=Name}"/>
<Label Width="200" Content="{Binding Path=PropertyType}" Foreground="Gray" FontStyle="Italic"/>
<!--<TextBox Text="{Binding Path=PropertyValue, Mode=TwoWay}"/>-->
<ContentControl VerticalContentAlignment="Center" Content="{Binding Path=PropertyValue}">
<ContentControl.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=PropertyValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DockPanel}}, Path=DataContext}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:String}">
<TextBox Text="{Binding Path=PropertyValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DockPanel}}, Path=DataContext}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type sys:Int32}">
<TextBox Text="{Binding Path=PropertyValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DockPanel}}, Path=DataContext}"/>
</DataTemplate>
</ResourceDictionary>
</ContentControl.Resources>
</ContentControl>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Wpf - Xaml : Binding a list of TextBox in a List of String to Source

I want to create a list of TextBox in binding with ObservableCollection<String> in a ViewModel but i have a problem. The binding performs only by source to dialog but it not works by dialog to source
in my ViewModel i have
private ObservableCollection<String> _ListSashDim;
public ObservableCollection<String> ListSashDim
{
get { return _ListSashDim; }
set {
if (_ListSashDim != value)
{
_ListSashDim = value;
this.RaisePropertyChanged("ListSashDim");
}
}
}
the list is initialized as below
private ObservableCollection<string> createListSashPosition(int numAnte)
{
ObservableCollection<string> ls = new ObservableCollection<string>();
for (int i = 0; i < numAnte; i++)
{
ls.Add("X");
}
return ls;
}
in my Xaml i have a DataTemplate
<!--#region DataTemplate ListSashDim-->
<DataTemplate x:Key="DataTemplateSashDim">
<ItemsControl>
<StackPanel Orientation="Vertical" Width="116" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock FontSize="10" HorizontalAlignment="Center" Text="Sash Dim"></TextBlock>
<TextBox x:Name="tbSashDim" Width="90" Text="{Binding Path=.,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ></TextBox>
</StackPanel>
</ItemsControl>
</DataTemplate>
<!--#endregion-->
and an ItemsControl in a StackPanel
<StackPanel
Orientation="Horizontal"
HorizontalAlignment="Center"
VerticalAlignment="Center" FlowDirection="LeftToRight"
Margin="0,10,0,0">
<ItemsControl ItemsSource="{Binding ListSashDim, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Margin="0" ItemTemplate="{DynamicResource DataTemplateSashDim}" Padding="8" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
Thanks so much
Manuel

Nested binding with templates

I am creating 'Lessons' tab in my application. The problem is in displaying data.
Timetable(List<DayInfo>) is binded to ItemsControl. Each DayInfo is an item in this ItemsControl. I tried to bind Exams collection to nested ItemsControl placed in ItemTemplate, but It's not working.
I'd like to know what I'm doing wrong. I guess my Exams binding is the problem.
Timetable:
private List<DayInfo> timetable;
public List<DayInfo> Timetable
{
get { return timetable; }
set
{
timetable = value;
NotifyOfPropertyChange(() => Timetable);
}
}
There is DayInfo.cs:
public class DayInfo : IValue
{
public string DayName { get; }
public List<ExamEntry> Exams { get; }
...
}
ExamEntry.cs:
public class ExamEntry : DayEntry, IValue
{
public string Description { get; }
...
}
XAML code:
<ItemsControl
ItemsSource="{Binding Timetable}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
...
</GroupStyle.HeaderTemplate>
<GroupStyle.Panel>
<ItemsPanelTemplate>
<StackPanel
Orientation="Horizontal"
Margin="0">
</StackPanel>
</ItemsPanelTemplate>
</GroupStyle.Panel>
</GroupStyle>
</ItemsControl.GroupStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label
Content="{Binding DayName}" /> <!-- It still works -->
<ItemsControl
ItemsSource="{Binding Exams}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label
Content="{Binding Description}" /> <!-- It's not displayed -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Everything is fine with this piece of code. I've forgotten about another part of project, where Timetable was bugged. Sorry for confusion.

ListView of radio buttons property set doesn't hit

this is the data template using my ViewModel
<DataTemplate DataType="{x:Type viewModels:MultipleKeysSectionViewModel}" >
<views:TabListItemUserControl Text="{Binding Header}"/>
</DataTemplate>
here is my list of radio buttons
<ListView ItemsSource="{Binding Tabs.Keys}" Grid.Column="1" HorizontalAlignment="Right" Visibility="{Binding IsMultiple, Converter={StaticResource boolToVis}} SelectedItem="{Binding SelectedParameterName}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding}" Margin="0,0,5,0" GroupName="FilterParameters"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListView>
here is my property
private string _selectedParameterName;
public string SelectedParameterName
{
get { return _selectedParameterName; }
set
{
_selectedParameterName = value;
RaisePropertyChanged(() => SelectedParameterName);
}
}
this is my the collection I bind the ListView into
public Dictionary<string, RegisterListViewModel> Tabs { get; set; }
however when I clicked the radio button it doesn't go to Set of the SelectedParameterName property
only when i hit the "box" containing the name of the radio button Set is executed.
Has anyone come across this issue before ?
the IsChecked property of the RadioButton is not bound to anything, therefore clicking on the RadioButton will do nothing.
Change your RadioButton to this:
<RadioButton Content="{Binding}" Margin="0,0,5,0"
IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListViewItem}}"/>

Grouping with strongly type group key in WPF

Every example and I article I found, is about grouping items by one property and displaying it. But what I have, is a strongly type group key, which I want to display. Here is models and grouping logic:
The item interface
public interface IItem {
string Title { get; }
string ToolTip { get; }
object Icon { get; }
Type GroupType { get; }
}
IItem has many implementations like this:
public class Item : IItem {
public string Title { get; private set; }
public string ToolTip { get; private set; }
public object Icon { get; private set; }
// I have many implementation of IGroup which I will use them in GroupType properties.
public Type GroupType { get { return SomeGroupTypeHere; } }
}
And here is the group interface:
public interface IGroup {
string Name { get; }
object Icon { get; }
}
and it has many implementations too.
I collect them in my view model (by getting help from Autofac):
public class MyViewModel {
private readonly IEnumerable<IGrouping<IGroup, IItem>> _items;
public MyViewModel(IEnumerable<IGroup> groups, IEnumerable<IItem> items){
_items = items.GroupBy(t => {
var g = groups.First(u => u.GetType() == t.GroupType);
return g;
});
}
public IEnumerable<IGrouping<IGroup, IItem>> Items {
get { return _items; }
}
}
Now, the problem is, how to display this grouped items, in a ItemsControl?
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate >
<!-- here is my template that uses IItem properties, example: -->
<Button Content="{Binding Title}"
ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</ItemsControl.ItemTemplate>
</ItemsControl>
The code, just only displays the first item in each group, and I have no idea what to do to show group headers (that use IGroup properties) and also show all items in each group. Any suggestion please? Any article or blog-post will be very useful. Thanks in advance.
You want to use HierarchicalDataTemplate when you want to display Grouped data, Change your ItemsControl.ItemTemplate to a `HierarchialDataTemplate'.
Sample (and Untested) HierarchialDataTemplate:
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding}">
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate >
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Refer to this blogpost for step-by-step approach to HierarchialDataTemplates.
UPDATE
I have tried the above HierarchialDataTemplate with your code and it doesnt seem to work. However, if I replace ItemsControl with TreeView, it does work as expected.
So, I guess, you might want to have nested ItemControl for your case, Tested sample code as below:
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
<ItemsControl ItemsSource="{Binding}" Margin="15,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the screen shot with this nested ItemsControls.
UPDATE 2
I think you need to update the template to display Group, the current template doesnt do that.
Updated Template is below:
<ItemsControl ItemsSource="{Binding Items}" Margin="20 20 20 0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Button Content="{Binding Key.Name}" />
<ItemsControl ItemsSource="{Binding}" Margin="15,0,0,0">
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Content="{Binding Title}" ToolTip="{Binding ToolTip}"/>
</DataTemplate >
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And the resulting Screenshot:

Categories