I have a UserControl called ModuleButton. This UC has some DependencyProperties and some public ICommand properties. I am trying to get the button to execute the two ICommand's I have when the button is clicked and the checkbox is checked. However clicking them doesn't do anything.
I first was using the click events on both the button and the checkbox, however when calling the property "Module" it would say its null. In the spirit of trying to do this as MVVM I moved them to RelayCommands thinking maybe its a DataContext issue. I know that the DependencyProperties are working because I am bound to them on the XAML side and everything looks fine. I even bound my checkbox text to display my "Module" property with a ToString converter as I was convinced that wasn't being bound properly, and it is. I haven't been able to find anything with my specific issue. I have a suspicion that it has to do with my ICommand properties aren't in the same context as my DependencyProperties.
ModuleButton.xaml
All the bindings work here except the none DependencyProperties bindings. I currently don't have the checkbox command bound as I'm trying to troubleshoot the button command first.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Button Style="{DynamicResource BaseButton}" Command="{Binding ModuleButtonClickCommand}" Content="{Binding Text}" Margin="5"/>
<DockPanel Grid.Row="1" Margin="0,0,0,4">
<CheckBox VerticalAlignment="Center" Content="{Binding Module, Converter={StaticResource ToString}}" IsChecked="{Binding IsChecked}" HorizontalAlignment="Left" Foreground="#FFE8E8E8" Margin="0" Style="{DynamicResource myCheckboxStyle}"/>
<Image Source="/Resources/Info-24.png" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,5,0" ToolTip="{Binding Description}"/>
</DockPanel>
</Grid>
ModuleButton.xaml.cs
RelayCommands are never executed, they work in other parts of my program that aren't mixed in with DependencyProperties.
public partial class ModuleButton : UserControl
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(ModuleButton),
new PropertyMetadata("Default Text")
);
public static readonly DependencyProperty DescriptionProperty = DependencyProperty.Register(
"Description", typeof(string), typeof(ModuleButton),
new PropertyMetadata("Default Description")
);
public static readonly DependencyProperty PageProperty = DependencyProperty.Register(
"Page", typeof(Uri), typeof(ModuleButton),
new PropertyMetadata(null)
);
public static readonly DependencyProperty ModuleProperty = DependencyProperty.Register(
"Module", typeof(IModule), typeof(ModuleButton),
new PropertyMetadata(null)
);
public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register(
"IsChecked", typeof(bool), typeof(ModuleButton),
new PropertyMetadata(false)
);
public string Text { get => (string)GetValue(TextProperty); set => SetValue(TextProperty, value); }
public string Description { get => (string)GetValue(DescriptionProperty); set => SetValue(DescriptionProperty, value); }
public Uri Page { get => (Uri)GetValue(PageProperty); set => SetValue(PageProperty, value); }
public IModule Module { get => (IModule)GetValue(ModuleProperty); set => SetValue(ModuleProperty, value); }
public bool IsChecked { get => (bool)GetValue(IsCheckedProperty); set => SetValue(IsCheckedProperty, value); }
public event EventHandler FavoriteIsCheckedChanged;
public ModuleButton()
{
InitializeComponent();
FavoriteCheckboxClickedCommand = new RelayCommand(param => FavoriteCheckboxClicked());
ModuleButtonClickCommand = new RelayCommand(param => ModuleButtonClick());
}
public ICommand FavoriteCheckboxClickedCommand { get; private set; }
public ICommand ModuleButtonClickCommand { get; private set; }
private void FavoriteCheckboxClicked()
{
FavoriteIsCheckedChanged?.Invoke(this, new EventArgs() { });
}
private void ModuleButtonClick()
{
MessageBox.Show("ModuleButtonClick");
}
}
This is the tab control its all displayed in.
<Page.DataContext>
<viewmodels:MainToolPageViewModel/>
</Page.DataContext>
<TabControl x:Name="Tabs" Margin="5" ItemsSource="{Binding Tabs}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding Content}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
This is how the Tabs list and its content area are populated.
var cats = ModuleManager.GetCategories();
foreach (var cat in cats.list.OrderBy(x => x.index))
{
var buttonsHost = new ModulesButtonHostView();
foreach (var mod in ModuleManager.LoadedModules.Where(x => x.Category == cat.name))
{
buttonsHost.Modules.Add(new ModuleItem {
Text = mod.Name,
Module = mod,
Description = mod.Description,
Page = mod.Page
});
}
Tabs.Add(new MyTabItem() {
Header = cat.name,
Content = buttonsHost
});
}
ModulesButtonHostView.xaml
Modules is a list of ModuleItems that have the same properties as the DependencyProperties on ModuleButton control.
<ItemsControl ItemsSource="{Binding Modules}" ScrollViewer.VerticalScrollBarVisibility="Auto" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ItemsControl.ItemTemplate>
<DataTemplate>
<controls:ModuleButton/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel FlowDirection="LeftToRight"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
What I'm expecting to happen is when I click the Module Button, it is able to use the Module property to load that module, which I have that part of the code working, it's the actual using the Module property in the RelayCommand that I can't figure out why its null.
Edit:
Adding image of issue with binding suggestion
You should set the source or the DataContext of the bindings to the UserControl itself:
<Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<Grid.RowDefinitions>
<RowDefinition Height="50*"/>
<RowDefinition Height="20*"/>
</Grid.RowDefinitions>
<Button Style="{DynamicResource BaseButton}" Command="{Binding ModuleButtonClickCommand}" Content="{Binding Text}" Margin="5"/>
<DockPanel Grid.Row="1" Margin="0,0,0,4">
<CheckBox VerticalAlignment="Center" Content="{Binding Module, Converter={StaticResource ToString}}" IsChecked="{Binding IsChecked}" HorizontalAlignment="Left" Foreground="#FFE8E8E8" Margin="0" Style="{DynamicResource myCheckboxStyle}"/>
<Image Source="/Resources/Info-24.png" HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,5,0" ToolTip="{Binding Description}"/>
</DockPanel>
</Grid>
If you don't, the runtime will look for these properties in the ModuleItem class.
Related
Background
I am making a custom control that has multiple ListBox's. I want to make this control MVVM compliant, so I am keeping any XAML and the code behind agnostic with respect to any ViewModel. One ListBox is simply going to be a list of TextBox's while the other is going to have a canvas as the host to display the data graphically. Both of these ListBox's are children of this custom control.
Pseudo example for the custom control template:
<CustomControl>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox1 Grid.Column="0"/>
<ListBox2 Grid.Column="1"/>
</CustomControl>
The code behind for this custrom control would have a dependency property that will serve as the ItemsSource, fairly standard stuff:
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(UserControl1), new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)));
private static void OnItemsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as UserControl1;
if (control != null)
control.OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue);
}
Where I am stuck
Because the two ListBox's are using the same data source but just display the data differently, I want the ItemsSource defined as one of the the parent view's dependency properties to be the ItemsSource for the two children. From the ViewModel side, this items source can be some sort of ObservableCollection<ChildViewModels>, or IEnumerable, or whatever it wants to be.
How can I point to properties from the ItemsSource's ViewModel to dependency properties of the child views?
I was hoping to get something similar to how it could be done outside of a custom view:
Example Parent ViewModel(omitting a lot, assume all functioning):
public class ParentViewModel
{
public ObservableCollection<ChildViewModel> ChildViewModels;
}
Example ViewModel (omitting INotifyPropertyChanged and associated logic):
public class ChildViewModel
{
public string Name {get; set;}
public string ID {get; set;}
public string Description {get; set;}
}
Example control (ommitting setting the DataContext, assume set properly):
<ListBox ItemsSource="{Binding ChildViewModels}">
<ListBox.ItemsTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text ="{Binding Description}"/>
</StackPanel>
</ListBox.ItemsTemplate>
</ListBox>
How can I do something similar where I can pass the properties from the ItemsSource to the child views on a custom control?
Many thanks
If I understand correctly what you need, then here is an example.
Add properties for element templates in both lists and style for Canvas.
using System.Collections;
using System.Windows;
using System.Windows.Controls;
namespace Core2022.SO.jgrmn
{
public class TwoListControl : Control
{
static TwoListControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TwoListControl), new FrameworkPropertyMetadata(typeof(TwoListControl)));
}
public IEnumerable ItemsSource
{
get { return (IEnumerable)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(
nameof(ItemsSource),
typeof(IEnumerable),
typeof(TwoListControl),
new PropertyMetadata((d, e) => ((TwoListControl)d).OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue)));
private void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
//throw new NotImplementedException();
}
public DataTemplate TemplateForStack
{
get { return (DataTemplate)GetValue(TemplateForStackProperty); }
set { SetValue(TemplateForStackProperty, value); }
}
public static readonly DependencyProperty TemplateForStackProperty =
DependencyProperty.Register(
nameof(TemplateForStack),
typeof(DataTemplate),
typeof(TwoListControl),
new PropertyMetadata(null));
public DataTemplate TemplateForCanvas
{
get { return (DataTemplate)GetValue(TemplateForCanvasProperty); }
set { SetValue(TemplateForCanvasProperty, value); }
}
public static readonly DependencyProperty TemplateForCanvasProperty =
DependencyProperty.Register(
nameof(TemplateForCanvas),
typeof(DataTemplate),
typeof(TwoListControl),
new PropertyMetadata(null));
public Style StyleForCanvas
{
get { return (Style)GetValue(StyleForCanvasProperty); }
set { SetValue(StyleForCanvasProperty, value); }
}
public static readonly DependencyProperty StyleForCanvasProperty =
DependencyProperty.Register(
nameof(StyleForCanvas),
typeof(Style),
typeof(TwoListControl),
new PropertyMetadata(null));
}
}
In the theme (Themes/Generic.xaml), set bindings to these properties:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:jgrmn="clr-namespace:Core2022.SO.jgrmn">
<Style TargetType="{x:Type jgrmn:TwoListControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type jgrmn:TwoListControl}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
ItemsSource="{TemplateBinding ItemsSource}"
ItemTemplate="{TemplateBinding TemplateForStack}"/>
<ListBox Grid.Column="1"
ItemsSource="{TemplateBinding ItemsSource}"
ItemTemplate="{TemplateBinding TemplateForCanvas}"
ItemContainerStyle="{TemplateBinding StyleForCanvas}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ListBox>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Window with an example of use:
<Window x:Class="Core2022.SO.jgrmn.TwoListWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Core2022.SO.jgrmn"
mc:Ignorable="d"
Title="TwoListWindow" Height="250" Width="400">
<FrameworkElement.DataContext>
<CompositeCollection>
<Point>15 50</Point>
<Point>50 150</Point>
<Point>150 50</Point>
<Point>150 150</Point>
</CompositeCollection>
</FrameworkElement.DataContext>
<Grid>
<local:TwoListControl ItemsSource="{Binding}">
<local:TwoListControl.TemplateForStack>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="{}Point ({0} {1})">
<Binding Path="X"/>
<Binding Path="Y"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</local:TwoListControl.TemplateForStack>
<local:TwoListControl.TemplateForCanvas>
<DataTemplate>
<Ellipse Width="10" Height="10" Fill="Red"/>
</DataTemplate>
</local:TwoListControl.TemplateForCanvas>
<local:TwoListControl.StyleForCanvas>
<Style TargetType="ListBoxItem">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
</Style>
</local:TwoListControl.StyleForCanvas>
</local:TwoListControl>
</Grid>
</Window>
You must spend all participating controls a ItemsSource property. The idea is to delegate the source collection from the parent to the child controls and finally to the ListBox. The ItemsSource properties should be a dependency property of type IList and not IEnumerable. This way you force the binding source to be of type IList which improves the binding performance.
To allow customization of the actual displayed items, you must either
a) spend every control a ItemTemplate property of type DataTemplate and delegate it to the inner most ListBox.ItemTemplate (similar to the ItemsSource property) or
b) define the template as a resource (implicit template, which is a key less DataTemplate).
The example implements a):
<Window>
<Window.DataContext>
<ParentViewModel />
</Window.DataCOntext>
<CustomControl ItemsSource="{Binding ChildViewModels}">
<CustomControl.ItemsTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text ="{Binding Description}"/>
</StackPanel>
</CustomControl.ItemsTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox1 Grid.Column="0"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ItemsSource}"
ItemTemplate="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ItemTemplate}" />
<ListBox2 Grid.Column="1"
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ItemsSource}"
ItemTemplate="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ItemTemplate}" />
</CustomControl>
</Window>
Inside the child controls (ListBox1 and ListBox2):
<UserControl>
<ListBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ItemsSource}"
ItemTemplate="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ItemTemplate}" />
</UserControl>
I'm tryin to make some wpf, which displays my user control for every day of some date range. So far ive tryied datagrid, but it does not support different data type of rows, so I'm tryin to solve it by UserControl and ItemsControl.
My UserControl - PeriodDayControl.xaml looks like this:
<UserControl x:Class="WpfApp.PeriodDayControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp"
mc:Ignorable="d" Height="168.443" Width="104.098">
<Grid>
<TextBox Text="{Binding Path=name}" HorizontalAlignment="Left" Height="23" Margin="20,10,21,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="63" TextAlignment="Center"/>
<TextBox Text="{Binding Path=price}" HorizontalAlignment="Left" Height="23" Margin="31,61,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="41" TextAlignment="Center"/>
<CheckBox IsChecked="{Binding Path=check}" Content="CheckBox" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="42,117,0,0" Height="16" Width="16" RenderTransformOrigin="0.372,0.449"/>
</Grid>
</UserControl>
And code to register properties for binding (PeriodDayControl.xaml.cs):
public PeriodDayControl()
{
InitializeComponent();
}
public string name
{
get { return (string)GetValue(nameProperty); }
set { SetValue(nameProperty, value); }
}
public static readonly DependencyProperty nameProperty =
DependencyProperty.Register("name", typeof(string), typeof(PeriodDayControl));
public decimal price
{
get { return (decimal)GetValue(priceProperty); }
set { SetValue(priceProperty, value); }
}
public static readonly DependencyProperty priceProperty =
DependencyProperty.Register("price", typeof(decimal), typeof(PeriodDayControl));
public bool check
{
get { return (bool)GetValue(checkProperty); }
set { SetValue(checkProperty, value); }
}
public static readonly DependencyProperty checkProperty =
DependencyProperty.Register("check", typeof(bool), typeof(PeriodDayControl));
So it look's like this in designer:
In MainWindow.xaml I'm trying to use Items control, to create template:
<ItemsControl x:Name="ItemsControlMain" ItemsSource="{Binding ViewModels}" Margin="0,166,0,0">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:PeriodDayControl name="{Binding Path=nameProperty}" price="{Binding Path=priceProperty}" check="{Binding Path=checkProperty}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and here is my try to create and populate data to PeriodDayControl:
List<PeriodDayControl> pdcList = new List<PeriodDayControl>();
pdcList.Add(new PeriodDayControl() { name = "01/01",price =(decimal)55, check=true});
pdcList.Add(new PeriodDayControl() { name = "02/01",price =(decimal)55, check=false});
pdcList.Add(new PeriodDayControl() { name = "03/01",price =(decimal)55, check=true});
ObservableCollection<PeriodDayControl> pdcColl = new ObservableCollection<PeriodDayControl>(pdcList);
ItemsControlMain.ItemsSource = pdcColl;
Here is how it looks now:
And finally here is what I'm trying to achive:
Right now it shows:
And here are my questions. Is it right decision to use ItemsControl? Or are there any other posibilities, to achive such funcionality? What's wrong with this solution(why it does not populate data)? To make context, I would like to change checkbox/price and then press button , which will read data from all of UserControl and save it to db.Thank you for any advice.
I have encountered a problem when binding values to a UserControl's DependencyProperty inside a DataTemplate. The binding is setting mentioned DependencyProperty to a reference to UserControl itself, instead of the right value. Since the DependencyProperty is type of string, this results in displaying UserControl's full class name.
<DataTemplate x:Key="FlagCellTemplate">
<ItemsControl ItemsSource="{Binding Flags}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:FlagView FlagString="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
However when I use any other control, like a Label for example, this works without a problem.
public partial class FlagView : UserControl, INotifyPropertyChanged {
public static readonly DependencyProperty FlagStringProperty = DependencyProperty.Register(
name: "FlagString",
propertyType: typeof(string),
ownerType: typeof(FlagView),
typeMetadata: new PropertyMetadata(
defaultValue: string.Empty,
propertyChangedCallback: OnFlagStringChanged
)
);
public string FlagString {
set => SetValue(FlagStringProperty, value);
get => (string) GetValue(FlagStringProperty);
}
public FlagView() {
InitializeComponent();
DataContext = this;
}
private static void OnFlagStringChanged(DependencyObject source, DependencyPropertyChangedEventArgs e) =>
((FlagView)source).NotifyPropertyChanged(e.Property.Name);
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
#endregion
}
A full source code demonstrating this problem can be found here: https://github.com/ProfileRedacted/UserControlBindingBug
Thank you for any help.
You should be able to simplify the code a bit. Here's the changes:
FlagView.xaml
<Grid>
<Label Content="{Binding}" Padding="0" Margin="4,0" />
</Grid>
FlagCellTemplate
<DataTemplate x:Key="FlagCellTemplate">
<ItemsControl ItemsSource="{Binding Flags}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:FlagView/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
FlagView.xaml.cs
public partial class FlagView : UserControl
{
public FlagView()
{
InitializeComponent();
}
}
This will give you the desired output:
I'm trying to add buttons to UserControl. I have to follow MVVM pattern. I have created a class, DeviceButton, with different set/gets and a constructor. In a viewmodel, used as datacontext, is the ObservableCollection and a get-method for the collection. I have bound the collection to a ItemsControl source and tried to add a template. I guess I'm missing something 'cause the buttons won't load.
The UserControl is added into a tab (using dragablz) which as well is a part of a ObservableCollection, also added at run time (this is working just fine). The idea is, that the tab has a list of buttons that has to be created at run time, where the list is fetched from a web service - so the buttons has to be added dynamically/programatically. Overview is just the first tab - a template for each tab (reflecting the fetched items) is being implemented when the buttons work. For now, I'm just adding a test button to the collection, but as stated, this won't show. What am I missing?
I have a Overview.xaml file:
<UserControl // attributes omitted to save spaces... ask if needed>
<StackPanel>
<Border>
<TextBlock FontSize="16" FontWeight="Bold" TextWrapping="WrapWithOverflow"
TextAlignment="Center" HorizontalAlignment="Center"
Foreground="{DynamicResource AccentColorBrush}">
Welcome to Greenhouse App
</TextBlock>
</Border>
<ItemsControl ItemsSource="{Binding DeviceButtons}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vmodel:DeviceButton}">
<Button Width="50" Height="50" Margin="10 10 0 0" Command="{Binding OpenTab}"
CommandParameter="{Binding DeviceType}" HorizontalAlignment="Left"
VerticalAlignment="Top" Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="{Binding ImageUrl}" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
// Manually added test button...
<Button x:Name="windMill" HorizontalAlignment="Left" Margin="10,0,0,0"
VerticalAlignment="Top" Width="50" Height="50"
Command="{Binding OpenTab}" FontFamily="Segoe Ui"
Style="{DynamicResource SquareButtonStyle}">
<StackPanel>
<Image Source="/Greenhouse;component/Icons/Windmill.png" />
</StackPanel>
</Button
</StackPanel>
I'm trying to add an ObservableCollection of type DeviceButton, the _deviceButtons collection (Binded as ItemsSource to the ItemsControl)
The tabList items are working just find, and I can manually add more if needed (these will later be added through the OpenNewTab-command, which should be bound to the buttons)
The DeviceButton file:
public class DeviceButton
{
private readonly string _content;
private readonly string _deviceType;
private readonly string _imageUrl;
public DeviceButton(string content, string deviceType, string imageUrl)
{
_content = content;
_deviceType = deviceType;
_imageUrl = imageUrl;
}
public string Content
{
get { return _content; }
}
public string DeviceType
{
get { return _deviceType; }
}
public string ImageUrl
{
get { return _imageUrl; }
}
}
The collection is located in a viewmodel file, MainWindowViewModel.cs:
public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
{
public ICommand OpenTab { get; set; }
private string tabControl { get; set; } = "0";
private IInterTabClient _interTabClient;
private ObservableCollection<TabContent> _tabContents = new ObservableCollection<TabContent>();
private ObservableCollection<DeviceButton> _deviceButtons = new ObservableCollection<DeviceButton>();
public MainWindowViewModel()
{
_interTabClient = new MyInterTabClient();
DeviceButtons.Add(new DeviceButton("Windturbine", "windturbine", "/Greenhouse;component/Icons/Windmill.png"));
TabContents.Add(new TabContent("Overview", new Overview()));
OpenTab = new RelayCommand<object>(OpenNewTab);
}
private void OpenNewTab(object obj)
{
MessageBox.Show("Yo"); // Not yet implemented
RaisePropertyChanged(() => tabControl);
}
public ObservableCollection<TabContent> TabContents
{
get { return _tabContents; }
}
public ObservableCollection<DeviceButton> DeviceButtons
{
get { return _deviceButtons; }
}
public IInterTabClient InterTabClient
{
get { return _interTabClient; }
}
}
The buttons aren't loaded when I start the program (only the "test" button added manually in the WPF).
The UserControl, Overview, is considered a tab in another Controls.Metrowindow, MainWindow.xaml:
<Window.Resources>
<Style TargetType="{x:Type dragablz:TabablzControl}">
<Setter Property="CustomHeaderItemTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type viewmodel:TabContent}">
<ContentPresenter Margin="4" Content="{Binding Content}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<dragablz:TabablzControl SelectedIndex="{Binding tabControl}" ItemsSource="{Binding TabContents}" x:Name="InitialTabablzControl" Margin="4 0 4 4">
<dragablz:TabablzControl.InterTabController>
<dragablz:InterTabController InterTabClient="{Binding MyInterTabClient}" />
</dragablz:TabablzControl.InterTabController>
</dragablz:TabablzControl>
I guess it's an issue with the resources/binding in the Overview.xaml, but I've exhausted all suggested solutions I could find.
Issue was in binding
ItemsSource="{Binding Path=DataContext.TabContents,
RelativeSource={RelativeSource AncestorLevel=1,
AncestorType={x:Type Window}, Mode=FindAncestor}}"
I suspect the DataContext is being set to a TabContent object, which does not have a DeviceButtons property
I would suggest using a tool like Snoop to verify your DataContext is what you expect.
I am creating custom control for menu and I have ListBox in my control. Something like this:
<ListBox x:Name="MenuItemsList"
Grid.Row="1"
ItemsSource="{Binding ProgramList, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Now when I want to catch Tap event, get properties from class and keep MVVM model. How can I catch this in MainPage.xaml.cs or in MainViewModel?
I have this code in my MainPage.xaml:
<controls:BottomMenu x:Name="BottomMenu" Canvas.Top="{Binding MenuCanvasTop}"
Width="480" Height="400">
</controls:BottomMenu>
I have prepare this code in my MainViewModel:
public RelayCommand<string> GoToSectionCommand
{
get
{
return goToArticleCommand
?? (goToArticleCommand = new RelayCommand<string>(
NavigateToSection));
}
}
But I don't know how can I call it. What's the best way?
Edit:
I tried to extend listbox:
<ListBox x:Name="MenuItemsList"
Grid.Row="1"
ItemsSource="{Binding ProgramList, Mode=OneWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<Button Content="{Binding Title}"
Command="{Binding ListButtonClickCommand, Source={RelativeSource Self}}"
CommandParameter="{Binding Url}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
With code-behind:
public ICommand ListButtonClickCommand
{
get { return (ICommand)GetValue(ListButtonClickCommandProperty); }
set { SetValue(ListButtonClickCommandProperty, value); }
}
public static readonly DependencyProperty ListButtonClickCommandProperty =
DependencyProperty.Register("ListButtonClickCommand", typeof(ICommand), typeof(BottomMenu), new PropertyMetadata(null));
public BottomMenu()
{
InitializeComponent();
}
Then in MainPage.xaml:
<controls:BottomMenu x:Name="BottomMenu" Canvas.Top="{Binding MenuCanvasTop}"
Width="480" Height="400"
ListButtonClickCommand="{Binding MenuItemButtonCommand}">
</controls:BottomMenu>
And in MainViewModel:
private ICommand menuItemButtonCommand;
public ICommand MenuItemButtonCommand
{
get
{
return menuItemButtonCommand
?? (menuItemButtonCommand = new RelayCommand(
NavigateToArticleSection));
}
}
For now without luck. It's not working. RelayCommand isn't triggered.
Edit2
I guess the problem is with binding command in custom control but I don't know how to fix it.
You just need to bind to the UserControl -- using "RelativeSource Self" will bind to the Button, which is not what you want. You should be able to use an "ElementName" binding to locate the user control:
<UserControl x:Name="UserControlName" ... >
...
<Button Content="{Binding Title}"
Command="{Binding ElementName=UserControlName,Path=ListButtonClickCommand}"
CommandParameter="{Binding Url}"/>
...
</UserControl>