How to bind StackPanel to a non ObservableCollection? - c#

<StackPanel Orientation="Vertical" x:Name="stackPanel">
<ItemsControl ItemsSource="{Binding C}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="Black" Text="{Binding prop1}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
I am binding this stack panel to a list with this code
List<C> list = new List<C>();
for (int i = 0; i < 100; i++)
{
list.Add(new C() { prop1 = "prop" });
}
stackPanel.DataContext = list;
And nothing happens. Do we have to always use ObservableCollections? Or what else might be wrong with the code?

The expression {Binding C} doesn't make sense, because you are putting the list itself into the DataContext. Then there is of course no property C.
You should replace it by this:
<ItemsControl ItemsSource="{Binding}">
That said, you aren't actually binding a StackPanel to an items collection, but the ItemsControl inside the StackPanel. If you intended to use the StackPanel as the ItemsPanel of the ItemsControl, you should write it like this:
<ItemsControl x:Name="itemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="Black" Text="{Binding prop1}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and set the ItemsSource like this:
itemsControl.ItemsSource = list;

Remove C in ItemsControl's ItemsSource like this:
<ItemsControl ItemsSource="{Binding}">

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>

How to bind commands to dynamically added buttons?

How can I bind a delegate command to a dynamically added UserControl button?
I have my UserControl button
<ItemsControl
ItemsSource="{Binding SomeCollection}"
HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid
Columns="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DMX_ControlLibrary:DMX_ItemBox
Width="250"
Height="150"
FontSize="12"
Command="{Binding ItemBoxButtonCommand}"
Content="{Binding Path=.}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And in my view model, I have this
private ICommand itemBoxButtonCommand;
public ICommand ItemBoxButtonCommand
{
get { return (this.itemBoxButtonCommand) ?? (this.itemBoxButtonCommand = new DelegateCommand(ItemButton_Click)); }
}
private void ItemButton_Click()
{
MessageBox.Show("");
}
Binding doesn't seem to work this way unlike it does on statically added controls.
How can I make it work?
As you stated in the comments for ItemBoxButtonCommand:
It's in the viewmodel that contains SomeCollection!
When the data template is applied for each item in the SomeCollection, each DMX_ItemBox will get its DataContext set the the corresponding item, not the data context of the ItemsControl which contains the SomeCollection.
In order to bind to the ItemBoxButtonCommand, you can use a RelativeSource binding to the DataContext of the ItemsControl (which is a parent of the items).
<ItemsControl.ItemTemplate>
<DataTemplate>
<DMX_ControlLibrary:DMX_ItemBox
Width="250"
Height="150"
FontSize="12"
Command="{Binding DataContext.ItemBoxButtonCommand, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
Content="{Binding Path=.}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
Alternatively, assign an x:Name to the ItemControl and refer to it with ElementName.
<ItemsControl
x:Name="MyItemsControl"
ItemsSource="{Binding SomeCollection}"
HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid
Columns="2" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<DMX_ControlLibrary:DMX_ItemBox
Width="250"
Height="150"
FontSize="12"
Command="{Binding DataContext.ItemBoxButtonCommand, ElementName=MyItemsControl}"
Content="{Binding Path=.}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

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.

Bind datagrid to an ObservableCollection list

I am new with WPF and I would like to bind from xaml to an ObservableCollection. Currently I am doing like this:
in xaml:
<Grid Grid.Column="1">
<local:ProcessingStep DataContext="{Binding ProcessingSteps[0]}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="2">
<local:ProcessingStep DataContext="{Binding ProcessingSteps[1]}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="3">
<local:ProcessingStep DataContext="{Binding ProcessingSteps[2]}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
in cs:
public ObservableCollection<ProcessingStepView> ProcessingSteps
{
get
{
return m_ProcessingSteps ??
(m_ProcessingSteps = new ObservableCollection<ProcessingStepViewl>
{
new ProcessingStepView("Step1"),
new ProcessingStepView("Step2"),
new ProcessingStepView("Step3")
});
}
}
How can I bind the list directly to wpf? e.g: if I have 100 steps, it is not nice to do one by one.
You can use ItemsControl with ItemTemplate and bind your list:
<ItemsControl ItemsSource="{Binding ProcessingSteps}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ProcessingStep />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ItemsControl is basically just a repeater, without selection, that will repeat ItemTemplate as many times as many items there are in ProcessingSteps and place them in whichever panel you choose. In this case horizontal StackPanel

WPF Empty Row in ItemsControl Binding with ObservableCollection

I have ItemsControl Binding to an ObservableCollection, every thing is ok except when ObservableCollection is empty, the ItemsControl will show one empty row !!
<ItemsControl Visibility="Visible" ItemsSource="{Binding ocItemsinInvoice,Mode=TwoWay}"
x:Name="test" Margin="10,-32,0,207" Width="412" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" VerticalAlignment="Top" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="btnOpenInvoice" Style="{StaticResource OpenInvoicesButton}"
FontSize="12" Width="300" Height="60" Foreground="#ff252526">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Item.ItemName}"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
How can I remove it?
public ObservableCollection<object> bindedObservableCollection
{
get{
ObservableCollection<object> newlist = new ObservableCollection<object>(yourObservableCollection);
return newlist;
}
}
I had this issue when binding to a list that was part of a datagrid, I found casting it to a new list of same type removed the blank editors record that is part of the datagrid.

Categories