I want to give the user the opportunity to set a collection of buttons in my CustomControl.
I tried to solve this with ItemsControl like this:
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type cc:MyCustomControl}}, Path=Buttons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Command}">
<Image Source="{Binding MyImageSource}"/>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Buttons DependencyProperty:
public static readonly DependencyProperty ButtonsProperty = DependencyProperty.Register(
"Buttons", typeof(IList), typeof(TileGrid), new PropertyMetadata(default(IList)));
public IList Buttons
{
get { return (IList) GetValue(ButtonsProperty); }
set { SetValue(ButtonsProperty, value); }
}
MyButton class:
public class MyButton: Button
{
public ImageSource MyImageSource { get; set; }
}
And how I want to see it in CustomControl implementation:
<cc:MyCustomControl>
<cc:MyCustomControl.Buttons>
<cc:MyButton Command="{Binding SignDocumentsCommand}"
MyImageSource="pack://application:,,,/CommonResources;component/Images/Icons/pen.png"/>
<cc:MyButton Command="{Binding DeleteDocumentsCommand}"
MyImageSource="pack://application:,,,/CommonResources;component/Images/Icons/remove.png"/>
</cc:MyCustomControl.Buttons>
</cc:MyCustomControl>
But it's not working. In live visual tree i see only MyButtons inside ItemsControl. Is this a right approach? Or i need to solve it another way?
The type that you are using for the Button items in the ItemsControl should not be derived from Button. You may use something simple like shown below. It is also not necessary to declare the Buttons property as dependency property. A simple ObservableCollection is sufficient.
public class MyButtonItem : DependencyObject
{
public static DependencyProperty CommandProperty = DependencyProperty.Register(
nameof(Command), typeof(ICommand), typeof(MyButtonItem));
public static DependencyProperty ImageSourceProperty = DependencyProperty.Register(
nameof(ImageSource), typeof(ImageSource), typeof(MyButtonItem));
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
}
public partial class MyCustomControl : UserControl
{
public MyCustomControl()
{
InitializeComponent();
}
public ObservableCollection<MyButtonItem> Buttons { get; }
= new ObservableCollection<MyButtonItem>();
}
The controls XAML would just be what you already have:
<ItemsControl ItemsSource="{Binding Buttons,
RelativeSource={RelativeSource AncestorType=UserControl}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Command}">
<Image Source="{Binding ImageSource}"/>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You would then add MyButtonItem objects to the Buttons collection:
<cc:MyCustomControl>
<cc:MyCustomControl.Buttons>
<cc:MyButtonItem
Command="{Binding SignDocumentsCommand}"
ImageSource="/CommonResources;component/Images/Icons/pen.png"/>
<cc:MyButtonItem
Command="{Binding DeleteDocumentsCommand}"
ImageSource="/CommonResources;component/Images/Icons/remove.png"/>
</cc:MyCustomControl.Buttons>
</cc:MyCustomControl>
Related
I want to make reusable jog user control. I added usercontrol on the main window.
I want to make like this
usercontrol button clicked -> 'UpJogClickCommand' call 'UpJogRelayCommand' -> execute method(UpJogMove)
But my code is Not working.. when i click button, main code do not execute 'UpJogMove'
[JogButtonControl.xaml]
<UserControl>
<Grid>
<Button Command="{Binding UpJogClickCommand}">
</Grid>
</UserControl>
[JogButtonControl.xaml.cs]
public partial class JogButtonControl : UserControl
{
public static readonly DependencyProperty UpJogClickCommandProperty =
DependencyProperty.Register(
"UpJogClickCommand",
typeof(ICommand),
typeof(JogButtonControl));
public ICommand UpJogClickCommand
{
get { return (ICommand)GetValue(UpJogClickCommandProperty); }
set { SetValue(UpJogClickCommandProperty, value); }
}
public JogButtonControl()
{
InitializeComponent();
}
}
[MainWindow.xaml]
<StackPanel x:Name="TempSP" Grid.Column="7">
<JogControl:JogButtonControl UpJogClickCommand="{Binding Path=UpJogRelayCommand}"
</StackPanel>
[MainWindow.xaml.cs]
private RelayCommand<object> _upJogRelayCommand;
public ICommand UpJogRelayCommand
{
get
{
if (_upJogRelayCommand == null)
{
_upJogRelayCommand = new RelayCommand<object>(UpJogMove);
}
return _upJogRelayCommand;
}
}
private void UpJogMove(object notUsed)
{
Debug.Print("UpJogExcuted():");
MoveToUpDirection();
}
You should specify the source object of the Binding in the XAML of your UserControl, e.g. by setting the RelativeSource property:
<UserControl>
<Grid>
<Button Command="{Binding UpJogClickCommand,
RelativeSource={RelativeSource AncestorType=UserControl}}">
</Grid>
</UserControl>
I have a UserControl with DependecyProperty:
public static readonly DependencyProperty OpenCommandProperty =
DependencyProperty.Register(
"OpenCommand",
typeof(ICommand),
typeof(BaseRouteFlatView),
new UIPropertyMetadata(null));
public ICommand OpenCommand
{
get { return (ICommand)GetValue(OpenCommandProperty); }
set { SetValue(OpenCommandProperty, value); }
}
In Xaml:
<UserControl x:Name="myUserControl">
<StackPanel>
<Button x:Name="first" Command="{Binding OpenCommand, ElementName=myUserControl}"/> <!--Command works-->
<controls:DropDownButtonControl>
<controls:DropDownButtonControl.DropDownContent>
<Button x:Name="second" Command="{Binding OpenCommand, ElementName=myUserControl}"/> <!--Command doesn't work-->
</controls:DropDownButtonControl.DropDownContent>
</controls:DropDownButtonControl>
</StackPanel>
</abstractions:UserControlBase>
What source I have to specify for working command in second button?
Try the following:
public UserControl()
{
InitializeComponent();
NameScope.SetNameScope(second, NameScope.GetNameScope(this));
}
I made a UserControl called ObjectExplorer :
public partial class ObjectExplorer : UserControl
{
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(List<Node>), typeof(ObjectExplorer), new PropertyMetadata(new List<Node>(), ItemsSet));
public List<Node> Items
{
get { return (List<Node>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
}
Inside the XAML of the control it has :
<TreeView Name="treeView" ItemsSource="{Binding Items}" Padding="0,5,0,0" Grid.Row="1">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Nodes}">
<StackPanel Orientation="Horizontal" Margin="4,3" ContextMenu="{Binding Converter={StaticResource NodeTypeToContextMenuConverter}}">
<Image Source="{Binding Converter={StaticResource StringToImageConverter}}" />
<TextBlock Text="{Binding Title}" Padding="4,0,0,0" />
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
So we can see, the TreeView is bound to the Items DP. Now I am using it in my MainWindow.xaml like so :
<fe:ObjectExplorer x:Name="objExplorer" Items="{Binding DatabaseObjects}" />
So this "instance" of the control, its Items is bound to the DP on MainWindow :
public partial class MainWindow : Window
{
public static readonly DependencyProperty DatabaseObjectsProperty = DependencyProperty.Register("DatabaseObjects", typeof(List<Node>), typeof(MainWindow), new PropertyMetadata(new List<Node>(), DatabaseObjectsPropertySet));
public List<Node> DatabaseObjects
{
get { return (List<Node>)GetValue(DatabaseObjectsProperty); }
set { SetValue(DatabaseObjectsProperty, value); }
}
}
Inside a method on MainWindow.cs I am doing :
List<Node> nodes = new List<Node>();
nodes.add(....);
DatabaseObjects = nodes;
What happens :
The DP on MainWindow triggers its set callback fine.
What does not happen :
The DP on ObjectExplorer does not get called, and nothing is displayed in the TreeView. I got it to work by naming the instance of the custom control (objExplorer) in mainwindow, then manually setting its Items whenever the callback for DatabaseObjects triggers...
private static void DatabaseObjectsPropertySet(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((MainWindow)d).objExplorer.Items = (List<Node>)e.NewValue;
}
I would like to know... why?
EDIT:
The MainWindow's DataContext is set in XAML DataContext="{Binding RelativeSource={RelativeSource Self}}
I expect :
Code in MainWindow -> MainWindow.DatabaseObjects DP ||breaks|| ---(binding)--> objExplorer.Items ---> UC.Items ---(Internal Binding)--> TreeView.ItemSource
I have an ItemsControl with ItemsSource binded to a list of SystemModels. It has to generate an usercontrol for every system in the list. And in those usercontrols it has some textboxes that show the name, is and location of the system.
My code creates the usercontrols but doesn't fill the textboxes in the usercontrol.
View:
<UserControl x:Name="SystemListScreen">
<ScrollViewer Grid.Row="1">
<ItemsControl x:Name="SystemList" ItemsSource="{Binding Path=Systems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="SystemModel">
<Widgets:MultiLineButton partID="{Binding ID}"
company="{Binding ItemsSource.Company}"
typeSorter="{Binding ItemsSource.Name, ElementName=SystemList}"
typeLocation="{Binding ItemsSource.Location, ElementName=SystemList}"
buttonCommand="{Binding DataContext.navigateInspectList, ElementName=SystemListScreen}"
buttonCommandParameter="{Binding ItemsSource.ID, ElementName=SystemList}"/>
<!--<Button Content="{Binding ID}"/>-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</UserControl>
ViewModel:
private List<SystemModel> systems;
public SystemListViewModel()
{
Systems = new List<SystemModel>();
Systems = SystemAPI.Instance.GetSystems();
}
public string App { get; set; }
public List<SystemModel> Systems
{
get { return systems; }
private set
{
systems = value;
NotifyChanged("systems");
NotifyChanged("NoResultsFound");
}
}
multilinebutton code:
public static readonly DependencyProperty buttonCommandProperty = DependencyProperty.Register("buttonCommand", typeof(ICommand), typeof(MultiLineButton));
public static readonly DependencyProperty buttonCommandParameterProperty = DependencyProperty.Register("buttonCommandParameter", typeof(Object), typeof(MultiLineButton));
public static readonly DependencyProperty partIDProperty = DependencyProperty.Register("partID", typeof(String), typeof(MultiLineButton));
public static readonly DependencyProperty companyProperty = DependencyProperty.Register("company", typeof(String), typeof(MultiLineButton));
public static readonly DependencyProperty typeSorterProperty = DependencyProperty.Register("typeSorter", typeof(String), typeof(MultiLineButton));
public static readonly DependencyProperty typeLocationProperty = DependencyProperty.Register("typeLocation", typeof(String), typeof(MultiLineButton));
public MultiLineButton()
{
this.DataContext = this;
InitializeComponent();
}
public String partID
{
get { return (String)GetValue(partIDProperty); }
set { SetValue(partIDProperty, value); }
}
public String company
{
get { return (String)GetValue(companyProperty); }
set { SetValue(companyProperty, value); }
}
public String typeSorter
{
get { return (String)GetValue(typeSorterProperty); }
set { SetValue(typeSorterProperty, value); }
}
public String typeLocation
{
get { return (String)GetValue(typeLocationProperty); }
set { SetValue(typeLocationProperty, value); }
}
public ICommand buttonCommand
{
get { return (ICommand)GetValue(buttonCommandProperty); }
set { SetValue(buttonCommandProperty, value); }
}
public Object buttonCommandParameter
{
get { return (Object)GetValue(buttonCommandParameterProperty); }
set { SetValue(buttonCommandParameterProperty, value); }
}
what not works: partID="{Binding ID}", company="{Binding ItemsSource.Company}", typeSorter="{Binding ItemsSource.Name, ElementName=SystemList}", typeLocation="{Binding ItemsSource.Location, ElementName=SystemList}" and buttonCommandParameter="{Binding ItemsSource.ID, ElementName=SystemList}".
But if i use just a button as datatemplate with Content="{Binding ID}" It works Perfect, an d if i use the usercontrol outside the datatemplate it works also. But it will not work inside the datatemplate.
The error i get is this: "BindingExpression path error: 'Company' property not found on 'object' ''MultiLineButton' (Name='')'. BindingExpression:Path=Company; DataItem='MultiLineButton' (Name=''); target element is 'MultiLineButton' (Name=''); target property is 'company' (type 'String')"
How can i fix those bindings?
not sure, maybe you should remove DataType="SystemModel" from DateTemplate.
Or try to use simple textbox(Binding id) as DataTemplate, see if there is still empty.
If above didn't get you any help, try Snoop(snoopwpf.codeplex.com) see what happended.
try ObservableCollection:
private ObservableCollection<SystemModel> _systems = new ObservableCollection<SystemModel>();
public ObservableCollection<SystemModel> Systems { get { return _systems; } }
public SystemListViewModel()
{
var systems = SystemAPI.Instance.GetSystems();
foreach (var system in systems)
{
Systems.Add(system);
}
}
and xaml should be:
<UserControl x:Name="SystemListScreen">
<ScrollViewer Grid.Row="1">
<ItemsControl x:Name="SystemList" ItemsSource="{Binding Path=Systems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="4"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Widgets:MultiLineButton
partID="{Binding ID}"
company="{Binding Company}"
typeSorter="{Binding Name}"
typeLocation="{Binding Location}"
buttonCommand="{Binding DataContext.navigateInspectList,
ElementName=SystemListScreen}"
buttonCommandParameter="{Binding ItemsSource.ID,
ElementName=SystemList}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</UserControl>
As chao wang said:
remove DataType="SystemModel" because if you're using just one type of DataType as DataTemplate it's not necessary. and the correct syntax is DataType="vm:SystemModel" where vm is defined in a parent tag like : xmlns:vm="clr-namespace:MySolution.MyVmProject.MyFolder"
also, check these:
remove ItemsSource. from Bindings inside DataTemplate because it's just wrong.
double check all names in bindings because if they're wrong, a null value is considered during runtime and you never know.
check your dataContext make sure UserControl have its DataContext to the correct instance of type dependency object which has Systems in it. and make sure it remains that way.
I have a ListBox that is data bound to an ObservableCollection.
Inside the DataTemplate, I have this custom user control that I want to bind to the current item.
How do I do that?
What should the binding path be?
Model:
private ObservableCollection<MyItem> _items;
public ObservableCollection<MyItem> Items
{
get { return _items; }
set
{
if (_items.Equals(value))
return;
_items = value;
RaisePropertyChanged("Items");
}
}
XAML:
<ListBox ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock TextWrapping="Wrap" Text="{Binding Id}" Margin="2"/>
<TextBlock TextWrapping="Wrap" Text="{Binding Name}" Margin="2"/>
<uc:MyControl MyValue="{Binding <WHATSHOULDTHISBE>, Mode=TwoWay}" Margin="2"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
User control:
public partial class MyControl : UserControl
{
public MyItem MyValue
{
get { return (MyItem)GetValue(MyProperty); }
set { SetValue(MyProperty, value); }
}
public static readonly DependencyProperty MyProperty = DependencyProperty.Register("MyValue",
typeof(MyItem), typeof(MyControl),
new PropertyMetadata(
new PropertyChangedCallback(PropertyChanged)));
public MyControl()
{
InitializeComponent();
}
private static void PropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
MyControl c = obj as MyControl;
if (c != null)
{
// TODO: do something here
}
}
}
MyControl.DataContext property would already be bound to your item view model in this case.
Your MyItem is the data context - if you want to bind to some member of My Item then its:
Binding Path=my_item_member
To bind to MyItem in its entirety I think you want:
Binding Path=.
Your might also need a DataTemplate for your control (in addition to the you have for the ListBoxItems) that maps the MyItems members to your control fields.