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>
Related
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>
I have an ItemTemplateSelector that uses the typical C# code for logic, but that code never even gets called from my ItemsControl. Right now I have just stubbed in ONE of the Selector Display Templates for "StringTemplate" and I have those in my "Parameters" array, yet it never calls selector.
<Grid>
<ItemsControl ItemsSource="{Binding Parameters}">
<ItemsControl.Resources>
<local:ReportRun_LaunchView_ParameterTemplateSelector x:Key="TemplateSelector_FormDetails">
<local:ReportRun_LaunchView_ParameterTemplateSelector.StringTemplate>
<DataTemplate>
<fv:WatermarkTextBox
Grid.Row="1"
Grid.Column="0"
Margin="0,0,7,0"
fv:WpfMvmFocusManager.FocusKey="txtSearchNpi"
Text="{fv:OnChangedBinding Path=StringValue}"
WatermarkContent="{Binding DataContext.Name}" />
</DataTemplate>
</local:ReportRun_LaunchView_ParameterTemplateSelector.StringTemplate>
</local:ReportRun_LaunchView_ParameterTemplateSelector>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel
HorizontalAlignment="Stretch"
IsItemsHost="True"
Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl x:Name="items" ItemTemplateSelector="{StaticResource TemplateSelector_FormDetails}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
I am implementing User Controls in my application with MVVM structure. How can I bind different User Controls in a single page and show in a window screen.
Like below image. I want same as it is in the image.
ViewModel code
public RelayCommand OpenWidgetList => new RelayCommand(() =>
{
ControlList = new List<UserControl>();
ControlList.Add(ObjColumn1L);
ControlList.Add(ObjColumn1L);
ControlList.Add(ObjColumn1M);
ControlList.Add(ObjColumn1S);
ControlList.Add(ObjColumn1XL);
ControlList.Add(ObjColumn1XXL);
ControlList.Add(ObjColumn2L);
ControlList.Add(ObjColumn2M);
ControlList.Add(ObjColumn2S);
ControlList.Add(ObjColumn2XL);
ControlList.Add(ObjColumn2XXL);
ControlList.Add(ObjColumn3L);
ControlList.Add(ObjColumn3M);
ControlList.Add(ObjColumn3S);
ControlList.Add(ObjColumn4M);
ControlList.Add(ObjColumn4S);
}, true);
XAML Code
<UniformGrid Name="widgetData" Background="Black" VerticalAlignment="Top" Height="691" Margin="0,50,0,0">
<UniformGrid Columns="3">
<UniformGrid Rows="6">
<UniformGrid>
<ItemsControl ItemsSource="{Binding ControlList}" Name="UserControlColumn" Margin="4,0" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserControlColumn HorizontalAlignment="Left" Margin="2" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UniformGrid>
</UniformGrid>
</UniformGrid>
</UniformGrid>
</Grid>
Yes, I found answer.
By coding I tried much but can not do that but in a simple way I had done by XAML side. Nothing but copy the code and separately bind each user Control with only one data from the back-end in DataCollection variable. Without binding each User Control from the back-end codes.
Below are my XAML code of WPF file. where repetedly done for each User Control to bind in the form.
<UniformGrid Rows="1">
<ItemsControl ItemsSource="{Binding DataCollection}" Name="UserControlColumn1" Margin="4,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserControlColumn1 HorizontalAlignment="Left" Margin="2" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UniformGrid>
<UniformGrid Rows="1">
<ItemsControl ItemsSource="{Binding DataCollection}" Name="UserControlColumn2" Margin="4,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:UserControlColumn2 HorizontalAlignment="Left" Margin="2" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</UniformGrid>
<!--Same for other user Controls as above two...-->
Every user control has its own datacontext so in your main viewmodel you add a property for every usercontrol viewmodel and then you bind it. Something like this:
UserControl1ViewModel
{
....
}
MainViewModel
{
UserControl1ViewModel UserControl1ViewModel {get; private set;}
}
I have two user controls, WorkflowTileItemsControl and WorkflowTileControl. The WorkflowTileItemsControl hosts the WorkflowTileControl in an ItemsControl. However, there are dependency properties on the WorkflowTileControl that I would like to expose to anything using the WorkflowTileItemsControl. In order to do that here is ItemsControl code for WorkflowTileItemsControl.
<ItemsControl ItemsSource="{Binding Source={StaticResource WorkflowTilesViewSource}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding WorkflowTileControl, ElementName=ctrlWorkflowTileItems}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
However this doesn't seem to work. It is only showing the last item in the ItemsControl. Below is code that works, and is the functionality I'm looking for (minus hard coding all the dependency properties).
<ItemsControl ItemsSource="{Binding Source={StaticResource WorkflowTilesViewSource}}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<OrderCommon:WorkflowTileControl IsReadOnly="True" Margin="5" TasksTitle="Defects" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And this is what my calling code looks like.
<OrderCommon:WorkflowTileItemsControl WorkflowRequirementTypeCode="DISBURSEMENTDFCT" Margin="5" MinWidth="1000" MaxWidth="1250" HorizontalAlignment="Left">
<OrderCommon:WorkflowTileItemsControl.WorkflowTileControl>
<OrderCommon:WorkflowTileControl IsReadOnly="True" Margin="5" TasksTitle="Defects" />
</OrderCommon:WorkflowTileItemsControl.WorkflowTileControl>
</OrderCommon:WorkflowTileItemsControl>
I feel like there is some simple step I'm missing. I'm not sure if ContentPresenter is the right tool for the job. I haven't done anything like this in WPF before. Can someone please assist?
So after some days of research I've found a solution. The WorkflowTileItemsControl needs to expose a Dependency Property of a DataTemplate which will be bound to the ItemsTemplate for the ItemsControl. Here is the xaml for the WorkflowTileItemsControl:
<ItemsControl ItemsSource="{Binding Source={StaticResource WorkflowTilesViewSource}}" ItemTemplate="{Binding WorkflowTileTemplate, ElementName=ctrlWorkflowTileItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
And Here is the xaml for the calling control:
<OrderCommon:WorkflowTileItemsControl WorkflowRequirementTypeCode="DISBURSEMENTDFCT" Margin="5" Width="1130" HorizontalAlignment="Left">
<OrderCommon:WorkflowTileItemsControl.WorkflowTileTemplate>
<DataTemplate>
<OrderCommon:WorkflowTileControl IsReadOnly="True" Margin="5" TasksTitle="Defects" />
</DataTemplate>
</OrderCommon:WorkflowTileItemsControl.WorkflowTileTemplate>
</OrderCommon:WorkflowTileItemsControl>
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.