ContentControl.Content not binding correctly in MVVM - c#

I have an MVVM app. I want a collection of buttons to be represented from the ViewModel and be dynamic.
Which means I want to populate the window with controls from the ViewModel.
I tried creating a content control and binding it's Content property to a Grid which I will put buttons in. The binding did not work, it remains empty.
I tried binding it to a simple string, still nothing. I should mention that other simple bindings do work, so that's why it's weird.
The creation of the UserControl:
<TabControl HorizontalAlignment="Left" Height="696" Margin="429,0,0,32" VerticalAlignment="Bottom" Width="552" ItemsSource="{Binding TabCollection}">
<TabControl.Resources>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<cattab:CategoryTab/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>`
The binding in the UserControl:
<ContentControl HorizontalAlignment="Left" Height="286" Margin="98,152,0,-396" VerticalAlignment="Top" Width="313">
<ContentControl Content="{Binding Favorites}" Margin="0,30,0,0" VerticalAlignment="Top"/>
</ContentControl>`
MainViewModel:
//**** Initilize TabCollection with fake data (temporary)
TabCollection.Add(new CategoryTabViewModel { Header = "בדיקה11" });
TabCollection.Add(new CategoryTabViewModel { Header = "בדיקה2" });
UserControl ViewModel:
public CategoryTabViewModel()
{
SearchText = "bbbaaaa";
Favorites.Add(new Button());
}
The binding of SearchText works, on Favorites it's not

Try to use an ItemsControl
Here's a good tutorial: http://www.wpf-tutorial.com/list-controls/itemscontrol/
<ItemsControl HorizontalAlignment="Left" Height="286" Margin="98,152,0,-396" VerticalAlignment="Top" Width="313" Content="{Binding Favorites}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controlsToolkit:WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Use a WrapPanel to manage your layout: http://www.wpftutorial.net/WrapPanel.html

After reading your updates I can say that your code is not MVVM compliant because your ViewModel layer is aware of the View layer (you create Buttons in your ViewModel)
What you need to do:
XAML of your CategoryTab
<ItemsControl ItemsSource="{Binding Favorites, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button/><!--Show whatever you want here-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to create new Button every time an object is added to Favorites you will need to make Favorites an ObservableCollection.

Related

WPF bind to property of the ItemsPanel of an ItemsControl

I've written a code example below:
<StackPanel>
<TextBlock>Appointments today</TextBlock>
<ItemsControl ItemsSource="{Binding Appointments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<controls:CalendarMonthDayEventsItemsPanel OutsideViewportCount="..." />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border>
<TextBlock Text="{Binding Title}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<StackPanel Orientation="Horizontal">
<TextBlock>+</TextBlock>
<TextBlock Text="{Binding ......}" /> <!-- Should show number of items in ItemsControl that are outside view, using CalendarMonthDayEventsItemsPanel.OutsideViewportCount -->
<TextBlock>more items</TextBlock>
</StackPanel>
</StackPanel>
I Created a custom Panel, the CalendarMonthDayEventsItemsPanel, and I use that panel as the itemspanel of the ItemsControl. In the panel's layout code, I determine how many items (panel childs) are outside the bounds of the panel. The property OutsideViewportCount contains the number of items that are outside of the visible bounds of the CalendarMonthDayEventsItemsPanel.
Now I want to show the value of OutsideViewportCount in a TextBlock that's below the ItemsControl.
This means I have to create some kind of binding, but everything I tried doesn't work.
Does someone have a solution or a better approach to achieve the same result?
Maybe you can make use of Tag property of ItemsPanel to relay the value of ItemsPanel as a workaround.
<ItemsControl x:Name="A"
ItemsSource="{Binding Appointments}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:CalendarMonthDayEventsItemsPanel OutsideViewportCount="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=Tag, Mode=OneWayToSource}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
...
</ItemsControl>
<StackPanel Orientation="Horizontal">
<TextBlock>+</TextBlock>
<TextBlock Text="{Binding ElementName=A, Path=Tag, Mode=OneWay}"/>
<TextBlock>more items</TextBlock>
</StackPanel>

C# TabControl binding with linq groupby statement in WPF

I am new to WPF and I am trying to bind a List of grouped object to a tabControl and I just manage to get halfway there
Here is my C# Code In the constructor :
IEnumerable<Validation> validations = ReflectiveEnumerator.GetEnumerableOfType<Validation>().Where(validation => validation.IsActive);
tabControl.ItemsSource = validations.GroupBy(validation => validation.TabName);
and my xaml code is :
<TabControl x:Name="tabControl" Margin="10,10,10,37" ItemsSource="{Binding Groups}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding ValidationName}"/>
</WrapPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
and so I get a tab by group which is what I was expecting but within my wrap panel I only have 1 ValidationName per Tab, I was expecting multiple ones. what is missing to have the content panel Iterate through my group.
When I don't group by I have multiple tabItem with the same Name and that's not what I'm looking for.
My Validation class looks like :
public class Validation
{
public string ValidationName {get; private set;}
public string TabName{get; private set;}
public bool IsActive{get; private set;}
}
You could use an ItemsControl in the ContentTemplate of the TabControl:
<TabControl x:Name="tabControl" Margin="10,10,10,37" ItemsSource="{Binding Groups}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ValidationName}" Margin="10"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
The above sample markup will display a TextBlock per item in each group in a WrapPanel inside each TabItem.

WPF Data Binding ViewModel property to ListBox inside a User Control in code behind

I'm trying to bind dynamically, in code behind, a VM property (an obseravble collection) to image in listBox that sits inside a usercontrol I show on my window,
but it's not working.
This is the XAML of the user control:
<WrapPanel x:Name="panel" HorizontalAlignment="Center" Focusable="False" FocusManager.IsFocusScope="False">
<ListBox x:Name="MazeListBox" ItemsSource="{Binding}">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid x:Name="MazeUniformGrid" Columns="{Binding VM_MazeCols}" Rows="{Binding VM_MazeRows}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Image x:Name="Img" Margin="-6" Source="{Binding}" Focusable="False"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</WrapPanel>
This are the usercontrols in XAML of the Outer Window:
<Controls:MazeBoard1 x:Name="you" Margin="0,0,650,0" Grid.Column="0" Height="Auto" Width="Auto" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Controls:MazeBoard1 x:Name="other" Margin="650,0,0,0" Grid.Column="0" Height="Auto" Width="Auto" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center"/>
This is how I bind dynamically in the cs of the window:
Binding youBinding = new Binding
{
Path = new PropertyPath("VM_MazeDisplay"),
};
Binding otherBinding = new Binding
{
Path = new PropertyPath("VM_OtherMazeDisplay"),
};
you.MazeListBox.SetBinding( ContentControl.ContentProperty,youBinding);
other.MazeListBox.SetBinding(ContentControl.ContentProperty, otherBinding);
I'll appreciate your help.
Thank you
Everything looks weird on your XAML...but if you do the following:
you.MazeListBox.SetBinding(ListBox.DataContextProperty, youBinding)
or
you.SetBinding(UserControl.DataContextProperty, youBinding)
or even you.MazeListBox.SetBinding(ListBox.ItemsSourceProperty, youBinding) (you will have to remove the binding into your xaml).
You should have the expected results.
However, why doing a binding in this point and not just only setting the DataContext? Something like you.DataContext = VM_MazeDisplay (assuming the instance of the VM is called that way).
Also, why do you put your ListBox into a WrapPanel?

Is it possible to access a property from the control's view model from a listview item template?

I am creating a feature where as you navigate through menu items you are given breadcrumbs to go back. In order to create this in my Windows 8 app, I am generating a collection of items and adding on to that collection as I navigate through the menu.
The xaml code for displaying the breadcrumbs is:
<ListView VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="120,60,0,0"
ItemsSource="{Binding Parents}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text=">>" />
<Button Content="{Binding Name}" Command="{Binding OpenCommand}" CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
While this works, I am not 100% happy with the bindings of the button. The problem is I am invoking functionality that is already up and running on my overall control's view model, and this seems to require me to have an OpenCommand property on my inner item command.
Is it possible to bind my button's Command= attribute to a command on the control's overall view model, instead of the list item itself?
It should be possible by something like this:
<ListView x:Name="listView" ...>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text=">>" />
<Button Content="{Binding Name}"
Command="{Binding DataContext.OpenCommand, ElementName=listView}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
...
</ListView>

XAML layout ItemsControl

I have searched extensively on Google and am struggling to find an easy example to follow for an ItemsControl which I think I need to be able to do the following.
I currently have a Grid with a scrollable Listbox containing Checkboxes which works as expected using the following XAML layout
<Grid>
<ListBox ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Selections}" Margin="12,22,12,94">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Path=Item.SelectionName}">
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
I now want to add a TextBox to the right of each Checkbox so it's aligned horizontally with 1 Checkbox and 1 Textbox per row. I thought I would have to use an ItemsControl to allow two controls but using the ItemsControl as below I lose the ability to scroll
<Grid>
<ItemsControl ScrollViewer.VerticalScrollBarVisibility="Auto"
ItemsSource="{Binding Selections}" Margin="12,22,12,94">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}"
Content="{Binding Path=Item.SelectionName}">
</CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Also, if I try and add a textbox similar to
<DataTemplate>
<CheckBox IsChecked="{Binding sChecked}" Content="{Binding Path=Item.BookieName}" />
<TextBox />
</DataTemplate>
I get an error The object 'DataTemplate' already has a child and cannot add 'TextBox'
Essentially, I want it to look like this
Can anyone give me a few pointers on how to structure the controls in XAML to get my desired layout?
Just use a StackPanel in your DataTemplate and set the orientation to horizontal.
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding sChecked}" Content="{Binding Path=Item.BookieName}" />
<TextBox />
</StackPanel>
</DataTemplate>

Categories