Bound CommandParameter on MenuItem is null - wpf

I don't understand why my commandparameter is null when all of the rest of the bindings seem to work, the correct image is displayed, the text is correct and the command CanExecute is called, but for some reason the parameter is null.
<MenuItem Header="Open Recent" ItemsSource="{Binding Path=MRU}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding URI}" />
<Setter Property="Header" Value="{Binding URI}" />
<Setter Property="Icon">
<Setter.Value>
<Image Source="{Binding URIImage}" />
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
and the view model class:
public class MRU_ViewModel
{
public MRU_ViewModel(string uri)
{
this.URI = uri;
Command = new Commands.Open();
}
public string URI { get; private set; }
public System.Windows.Media.Imaging.BitmapImage URIImage { get { return WorkSpace.GetURIImage(URI); } }
public ICommand Command { get; private set; }
}
and the CanExecute of the command looks like this:
public bool CanExecute(object parameter)
{
return parameter != null && parameter is string; // <---parameter is null
}

Related

How to switch Control by DataTrigger in the xaml?

There is a window consist of the two control.
one is TreeView, the other is ListBox.
The code is as shown below.
<Grid Grid.Row="1">
<TreeView x:Name="treeView" BorderThickness="0" Visibility="Visible"
ItemsSource="{Binding TotalCPUs}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<mvvm:EventToCommand Command="{Binding CPUSelectedCommand}"
PassEventArgsToCommand="True"
EventArgsConverter="{localConverters:SelectedItemConverter}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
<ListBox x:Name="listBox" BorderThickness="0" Visibility="Collapsed"/>
</Grid>
Now I would like to change the value of the Visibility property of the Control whenever changes property of the ViewModel. (FilterMode True = ListBox Visible, FilterMode False = TreeView = Visible)
To this, I modified my XAML code as below.
<Grid Grid.Row="1">
<TreeView x:Name="treeView" BorderThickness="0" Visibility="Visible"
ItemsSource="{Binding TotalCPUs}">
<TreeView.Style>
<Style TargetType="{x:Type TreeView}">
<Style.Triggers>
<DataTrigger Binding="{Binding FilterMode}" Value="true">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.Style>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<mvvm:EventToCommand Command="{Binding CPUSelectedCommand}"
PassEventArgsToCommand="True"
EventArgsConverter="{localConverters:SelectedItemConverter}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
<ListBox x:Name="listBox" BorderThickness="0" Visibility="Collapsed">
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding FilterMode}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
</Grid>
Below is the ViewModel code.
public class NewProjectViewModel : DialogViewModel
{
private Generator projectGenerator = new Generator();
public ObservableCollection<ClassHierarchyData> TotalCPUs { get; private set; } = new ObservableCollection<ClassHierarchyData>();
public ObservableCollection<DetailType> FilterCPUs { get; private set; } = new ObservableCollection<DetailType>();
private bool filterMode;
public bool FilterMode
{
get => filterMode;
set
{
if (this.filterMode == value) return;
this.filterMode = value;
this.RaisePropertyChanged("FilterMode");
}
}
private string cpuSearch;
public string CPUSearch
{
get => this.cpuSearch;
set
{
if (this.cpuSearch == value) return;
this.cpuSearch = value;
this.FilterCPUs.Add(new DetailType(typeof(Target), "abc"));
}
}
private Type selectedTerminalItem;
public Type SelectedTerminalItem
{
get => this.selectedTerminalItem;
private set
{
if (this.selectedTerminalItem == value) return;
this.selectedTerminalItem = value;
this.RaisePropertyChanged("SelectedTerminalItem");
}
}
private Type selectedItem;
public Type SelectedItem
{
get => selectedItem;
set
{
if (this.selectedItem == value) return;
this.selectedItem = value;
this.RaisePropertyChanged("SelectedItem");
CreateCommand.RaiseCanExecuteChanged();
}
}
private string solutionName = string.Empty;
public string SolutionName
{
get => this.solutionName;
set
{
if (this.solutionName == value) return;
this.solutionName = value;
this.RaisePropertyChanged("SolutionName");
this.RaisePropertyChanged("SolutionFullPath");
CreateCommand.RaiseCanExecuteChanged();
}
}
private string solutionPath = string.Empty;
public string SolutionPath
{
get => this.solutionPath;
set
{
if (this.solutionPath == value) return;
this.solutionPath = value;
if(this.SolutionPath.Length > 0)
{
if (this.solutionPath.Last() != '\\')
this.solutionPath += "\\";
}
this.RaisePropertyChanged("SolutionPath");
this.RaisePropertyChanged("SolutionFullPath");
CreateCommand.RaiseCanExecuteChanged();
}
}
public bool CreateSolutionFolder { get; set; }
public string SolutionFullPath { get => this.SolutionPath + this.solutionName; }
private RelayCommand searchCommand;
public RelayCommand SearchCommand
{
get
{
if (this.searchCommand == null) this.searchCommand = new RelayCommand(this.OnSearch);
return this.searchCommand;
}
}
private void OnSearch()
{
CommonOpenFileDialog selectFolderDialog = new CommonOpenFileDialog();
selectFolderDialog.InitialDirectory = "C:\\Users";
selectFolderDialog.IsFolderPicker = true;
if (selectFolderDialog.ShowDialog() == CommonFileDialogResult.Ok)
{
this.SolutionPath = selectFolderDialog.FileName + "\\";
}
}
private RelayCommand<Action> _createCommand;
public RelayCommand<Action> CreateCommand
{
get
{
if (this._createCommand == null)
this._createCommand = new RelayCommand<Action>(this.OnCreate, this.CanExecuteCreate);
return this._createCommand;
}
}
private void OnCreate(Action action)
{
projectGenerator.GenerateSolution(this.SolutionPath, this.SolutionName, this.CreateSolutionFolder);
action?.Invoke();
}
private bool CanExecuteCreate(Action action)
{
if (this.SelectedTerminalItem == null) return false;
if (string.IsNullOrEmpty(this.solutionPath)) return false;
if (string.IsNullOrEmpty(this.solutionName)) return false;
return true;
}
private RelayCommand<ClassHierarchyData> cpuSelectedCommand;
public RelayCommand<ClassHierarchyData> CPUSelectedCommand
{
get
{
if (this.cpuSelectedCommand == null)
this.cpuSelectedCommand = new RelayCommand<ClassHierarchyData>(OnCPUSelected);
return this.cpuSelectedCommand;
}
}
private void OnCPUSelected(ClassHierarchyData selected)
{
this.SelectedItem = selected.Data;
this.SelectedTerminalItem = (selected.Items.Count == 0) ? selected.Data : null;
}
private RelayCommand<string> navigateCommand;
public RelayCommand<string> NavigateCommand
{
get
{
if (this.navigateCommand == null)
this.navigateCommand = new RelayCommand<string>((uri) =>
{
Process.Start(new ProcessStartInfo(uri));
});
return navigateCommand;
}
}
public NewProjectViewModel()
{
ClassHierarchyGenerator classHierarchyGenerator = new ClassHierarchyGenerator();
this.TotalCPUs.Add(classHierarchyGenerator.ToHierarchyData(typeof(Target)));
this.FilterCPUs.CollectionChanged += FilterCPUs_CollectionChanged;
}
private void FilterCPUs_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
this.FilterMode = (this.FilterCPUs.Count > 0) ? true : false;
}
}
public class DetailType
{
public Type Type { get; }
public string Path { get; }
public DetailType(Type type, string path)
{
Type = type;
Path = path;
}
}
If user input data to the TextBox for filtering then the value of the CPUSearch is changed.
If the value of the CPUSearch is changed then be added test value into the FilterCPUs. (Note CPUSearch property)
when being added value into the FilterCPUs, FilterCPUs_CollectionChanged is called and the value of the FilterMode is changed.
But the above code does not work although be changed the value of the FilterMode. (works well except for functionality related to FilterMode)
Why doesn't the Control switch?
Thanks for reading.
I solved this problem by referring WPF Showing / Hiding a control with triggers
My XAML code updated like this and it works well.
Thanks for your interest.
<Grid Grid.Row="1">
<TreeView x:Name="treeView" BorderThickness="0"
ItemsSource="{Binding TotalCPUs}">
<TreeView.Style>
<Style TargetType="{x:Type TreeView}">
<Style.Triggers>
<DataTrigger Binding="{Binding FilterMode}" Value="true">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding FilterMode}" Value="false">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TreeView.Style>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectedItemChanged">
<mvvm:EventToCommand Command="{Binding CPUSelectedCommand}"
PassEventArgsToCommand="True"
EventArgsConverter="{localConverters:SelectedItemConverter}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TreeView>
<ListBox x:Name="listBox" BorderThickness="0"
ItemsSource="{Binding FilterCPUs}"
SelectedItem="{Binding SelectedItem}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock FontSize="8" Text="{Binding Path=Path}" Margin="0 0 0 3"/>
<TextBlock Text="{Binding Type.Name}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Setter Property="Background" Value="{DynamicResource CommonEnableBackgroundBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource CommonEnableBorderBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource CommonEnableTextBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding FilterMode}" Value="true">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding FilterMode}" Value="false">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
</Grid>

Image content disappearing for a Label inside a DataTemplate [duplicate]

I am using Mahapp's DropDownButton
Here is what I have:
using
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
<mah:DropDownButton
Content="my button"
Icon="{DynamicResource appbar_projector_screen}"
ItemsSource="{Binding Path=MySource}">
<mah:DropDownButton.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Title}"/>
<Setter Property="Command" Value="{Binding Path=Command}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Id }"/>
<Setter Property="Icon">
<Setter.Value>
<Image Width="25" Height="25" Source="{Binding IconPath}" />
</Setter.Value>
</Setter>
</Style>
</mah:DropDownButton.ItemContainerStyle>
</mah:DropDownButton>
First I tried using:
<Setter Property="Header">
<Setter.Value>
<StackPanel>
<Image Width="20" Height="20" Source="{Binding IconPath}" />
<TextBlock Text="{Binding Path=Title}" />
</StackPanel>
</Setter.Value>
</Setter>
But that code does not show any image. So then I tried:
<Setter Property="Icon">
<Setter.Value>
<Image Width="25" Height="25" Source="{Binding IconPath}" />
</Setter.Value>
</Setter>
Initializing the Object MySource:
public List<SomeDefinition> MySource{ get; private set; }
MySource= new List<SomeDefinition>
{
new SomeDefinition{Id=1, Title= $#"1", Command = new RelayCommand<object>(SomeMethod1), IconPath = "D:\\ico1.png"},
new SomeDefinition{Id=2, Title= $#"2", Command = new RelayCommand<object>(SomeMethod2), IconPath = "D:\\ico2.png"},
new SomeDefinition{Id=3, Title= $#"3", Command = new RelayCommand<object>(SomeMethod3), IconPath = "D:\\ico3.png"},
new SomeDefinition{Id=4, Title= $#"4", Command = new RelayCommand<object>(SomeMethod4), IconPath = "D:\\ico4.png"},
new SomeDefinition{Id=5, Title= $#"5", Command = new RelayCommand<object>(SomeMethod5), IconPath = "D:\\ico5.png"}
};
having:
public class SomeDefinition
{
public int Id { get; set; }
public string Title{ get; set; }
public ICommand Command { get; set; }
public string IconPath { get; set; }
}
This shows image only for the last value on menu, I canĀ“t figure out what I am missing. Why is it showing only image for last record?
I tested with all images and they are written correctly, they also do exist, so issue is not finding the image file .
Update
So I have tried using #mm8 solution and changed definition Class to:
public class SomeDefinition
{
public int Id { get; set; }
public string Title{ get; set; }
public ICommand Command { get; set; }
public Image IconImage { get; set; }
}
having xaml:
<mah:DropDownButton
Content="my button"
Icon="{DynamicResource appbar_projector_screen}"
ItemsSource="{Binding Path=MySource}">
<mah:DropDownButton.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Title}"/>
<Setter Property="Command" Value="{Binding Path=Command}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Id }"/>
<Setter Property="Icon" Value="{Binding IconImage}" />
</Style>
</mah:DropDownButton.ItemContainerStyle>
</mah:DropDownButton>
and code behind:
var Image = new Bitmap(#"D:\\1.png");
MySource = new List<SomeDefinition>
{
new SomeDefinition{Id=1, Title= $#"1", Command = Command1, IconImage = Image},
new SomeDefinition{Id=2, Title= $#"2", Command = Command2, IconImage = Image},
new SomeDefinition{Id=3, Title= $#"3", Command = Command3, IconImage = Image},
new SomeDefinition{Id=4, Title= $#"4", Command = Command4, IconImage = Image},
new SomeDefinition{Id=5, Title= $#"5", Command = Command5, IconImage = Image}
};
Result is:
How to convert object to Image?
"This shows image only for the last value on menu" - Setter creates only one instance of Image, which can only be displayed in one place.
As a workaround you can declare Image as a non-shared resource, and use it via StaticResource in Setter:
<Image x:Key="StdIcon" x:Shared="False" Width="25" Height="25" Source="{Binding IconPath}" />
<Setter Property="Icon" Value="{StaticResource StdIcon}"/>

Listbox item trigger colour change

I am trying to change the colour of the items in a listbox based on a trigger using MVVM
<Border Grid.Row="1" Width="300" Margin="0,0,20,0" BorderThickness="1,2,1,1" CornerRadius="5" BorderBrush="#FF999393" Background="#FFE9EDF1" >
<ListBox ItemsSource="{Binding LogMessageList, UpdateSourceTrigger=PropertyChanged}" Background="{x:Null}" Margin="3" BorderBrush="{x:Null}" FontSize="13.333" >
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="#FF403E3E" />
<Style.Triggers>
<DataTrigger Binding="{Binding FatalError, UpdateSourceTrigger=PropertyChanged}" Value="Fatal">
<Setter Property="Foreground" Value="Firebrick" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I'm setting the property change correctly but nothing seems to change.
Thanks
EDIT:
Still stuck. Trying
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="#FF403E3E" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext.FatalError, UpdateSourceTrigger=PropertyChanged}" Value="Fatal">
<Setter Property="Foreground" Value="Firebrick" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
Tried your scenario. Please refer the below code example:
View Model
public class Vm : INotifyPropertyChanged
{
public ObservableCollection<VmUser> VmUsers { get; set; }
private string errorType;
public string ErrorType
{
get { return errorType; }
set
{
errorType = value;
Raise("ErrorType");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void Raise(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class VmUser
{
public string Name { get; set; }
public int Age { get; set; }
}
Set the DataContext of window in constructor :
public MainWindow()
{
Vm = new Vm
{
VmUsers = new ObservableCollection<VmUser>
{
new VmUser { Name = "Gil", Age = 1 },
new VmUser { Name = "Dan", Age = 2 },
new VmUser { Name = "John", Age = 3 },
},
ErrorType = "Fatal"
};
InitializeComponent();
DataContext = TheVm;
}
Defined ListBox in XAML as:
<ListBox Grid.Row="2" ItemsSource="{Binding VmUsers}" DisplayMemberPath="Name" SelectedValuePath="Age">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="#FF403E3E" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},Path=DataContext.ErrorType, UpdateSourceTrigger=PropertyChanged}" Value="Fatal">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
To test the scenario I have wrote a small test to toggle the ErrorType on click on button
private void Button_Click(object sender, RoutedEventArgs e)
{
var vm = this.DataContext as Vm;
if (vm != null)
{
if (vm.ErrorType == "Fatal")
{
vm.ErrorType = "Non Fatal";
}
else
{
vm.ErrorType = "Fatal";
}
}
}
With above example foreground color changes based on ErrorType.

DXGridControl Is not working CellStyle

I would like to change the Background, FontWeight, Foreground in dxg:GridColumn.
I wrote this code but unfortunately it does not work.
How can I fix it?
public class CellViewModel {
public string Value { get; set; }
public string FontWeight { get; set; }
public string Foreground { get; set; }
public string Background { get; set; }
}
public class TestItemViewModel {
public CellViewModel Column1 { get; set; }
public CellViewModel Column2 { get; set; }
public CellViewModel Column3 { get; set; }
}
public class TestViewModel {
public TestViewModel() {
this.Items = new ObservableCollection<TestItemViewModel> {
new TestItemViewModel {
Column1 = new CellViewModel { Value = "CCC", FontWeight = "Bold", Foreground = "Red", Background = "Green" },
Column2 = new CellViewModel { Value = "-683,84", FontWeight = "Normal", Foreground = "Red", Background = "SeaGreen" },
Column3 = new CellViewModel { Value = "-683,84", FontWeight = "Normal", Foreground = "Red", Background = "SeaGreen" },
}
};
}
public ObservableCollection<TestItemViewModel> Items { get; set; }
}
<dxg:GridControl HorizontalContentAlignment="Right"
AutoPopulateColumns="False"
ItemsSource="{Binding Path=Performance.Items}">
<dxg:GridControl.Resources>
<DataTemplate x:Key="GridColumnHorizontalContentAlignmentTemplate">
<dxe:TextEdit x:Name="PART_Editor" HorizontalContentAlignment="Right" />
</DataTemplate>
</dxg:GridControl.Resources>
<dxg:GridControl.Columns>
<dxg:GridColumn AllowColumnFiltering="False"
CellTemplate="{StaticResource GridColumnHorizontalContentAlignmentTemplate}"
FieldName="BuyAndHold.Value"
Header="Column 1"
HorizontalHeaderContentAlignment="Right">
<dxg:GridColumn.CellStyle>
<Style TargetType="dxg:CellContentPresenter">
<Setter Property="Background" Value="{Binding Path=Column1.Background}" />
<Setter Property="FontWeight" Value="{Binding Path=Column1.FontWeight}" />
<Setter Property="Foreground" Value="{Binding Path=Column1.Foreground}" />
</Style>
</dxg:GridColumn.CellStyle>
</dxg:GridColumn>
</dxg:GridControl.Columns>
<dxg:GridControl.View>
<dxg:TableView AllowEditing="False"
AllowGrouping="False"
AllowSorting="False"
NavigationStyle="None"
PrintTotalSummary="False"
ShowGroupPanel="False"
ShowHorizontalLines="False"
ShowIndicator="False"
ShowTotalSummary="False"
ShowVerticalLines="False" />
</dxg:GridControl.View>
</dxg:GridControl>
I can not understand why there is
<Style TargetType="dxg:CellContentPresenter">
<Setter Property="Background" Value="{Binding Path=BuyAndHold.Background}" />
<Setter Property="FontWeight" Value="{Binding Path=BuyAndHold.FontWeight}" />
<Setter Property="Foreground" Value="{Binding Path=BuyAndHold.Foreground}" />
</Style>
no data binding?
If I set the value manually everything works. For example.
<Setter Property="Background" Value="Red" />
Work Fine.
The binding's Path you are using is wrong.
Use the following syntax:
<Setter Property="Background" Value="{Binding Path=RowData.Row.BuyAndHold.Background}"/>
instead of:
<Setter Property="Background" Value="{Binding Path=BuyAndHold.Background}"/>
Related Example: How to Conditionally Apply Styles (CellStyle)

WPF Ribbon - Data templating in a RibbonTab

I've spent the better part of the last two days dinging around with this control and I'm stuck. Basically I don't how to data template it's RibbonTab. What I have would work for me if it would only not show it at the bottom of the RibbonTab! Grr!
What I have in my XAML is:
<r:Ribbon Grid.Row="0" Title="MSRibbon" x:Name="ribbon">
<r:Ribbon.Style>
<Style TargetType="{x:Type r:Ribbon}">
<Setter Property="TabHeaderTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Text="{Binding Header}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsSource" Value="{Binding AvailableRibbonTabs}"/>
<Setter Property="SelectedItem" Value="{Binding SelectedRibbonTab}"/>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type r:RibbonTab}">
<Setter Property="ItemsSource" Value="{Binding RibbonTabData}"/>
</Style>
</Setter.Value>
</Setter>
</Style>
</r:Ribbon.Style>
<r:Ribbon.Resources>
<DataTemplate DataType="{x:Type vmsrc:RecordingRibbonTabGroupData}">
<viewsrc:RecordingTabGroupControl/>
</DataTemplate>
</r:Ribbon.Resources>
</r:Ribbon>
The XAML of the control i would like to show in the ribbon tab group is (this, when displayed gets glued to the bottom of the ribbon tab):
<r:RibbonControl x:Class="Scanner.Views.RecordingRibbonTabGroupData">
<StackPanel Orientation="Horizontal">
<r:RibbonButton Label="foo" />
<r:RibbonButton Label="bar" />
<ListBox ItemsSource="{Binding Barcodes}" />
</StackPanel>
</r:RibbonControl>
Here I tried using different combinations of controls but to no effect. As the control base type I used the RibbonTab, the RibbonGroup, UserControl etc and I think I used every possible control as the main container, like StackPanel, Grid, ItemsControl, etc.. And also experimented with setting the Heights of every control and H/V alignment, etc. Nothing helped.
My view models are such (INPC is injected with INPCWeaver and it works):
public abstract class AbstractViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
public abstract class AbstractRibbonTab : AbstractViewModel
{
public string Header { get; set; }
public bool IsSelected { get; set; }
public ObservableCollection<AbstractRibbonTabGroupData> RibbonTabData { get; set; }
}
public class RecordingRibbonTab : AbstractRibbonTab
{
public RecordingRibbonTab()
{
this.Header = "Recording";
this.RibbonTabData = new ObservableCollection<AbstractRibbonTabGroupData>() { new RecordingRibbonTabGroupData() };
}
}
public class SessionRibbonTab : AbstractRibbonTab
{
public SessionRibbonTab()
{
this.Header = "Session";
this.RibbonTabData = new ObservableCollection<AbstractRibbonTabGroupData>() { new AbstractRibbonTabGroupData() };
}
}
public class SettingsRibbonTab : AbstractRibbonTab
{
public SettingsRibbonTab()
{
this.Header = "Settings";
this.RibbonTabData = new ObservableCollection<AbstractRibbonTabGroupData>() { new AbstractRibbonTabGroupData() };
}
}
The XAML has it's data context set to an instance of:
public class MainWindowViewModel : AbstractViewModel, IMainWindowViewModel
{
...
public ObservableCollection<AbstractRibbonTab> AvailableRibbonTabs { get; private set; }
public AbstractRibbonTab SelectedRibbonTab { get; set; }
...
public MainWindowViewModel(PinChangeCommand pcc)
{
this.AvailableRibbonTabs = new ObservableCollection<AbstractRibbonTab>();
this.AvailableRibbonTabs.Add(new RecordingRibbonTab());
this.AvailableRibbonTabs.Add(new SessionRibbonTab());
this.AvailableRibbonTabs.Add(new SettingsRibbonTab());
}
}
The bindings work.
As a side note, below the ribbon there is a content control declared like so
<ContentControl Grid.Row="1" Content="{Binding SelectedRibbonTab}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type vmsr:RecordingRibbonTab}">
<views:RecordingView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
that works perfectly fine as one would expect.
The 'recording' view that I did implement has the following XAML (it just shows the header, as one can see in the screenshot below):
<UserControl x:Class="Scanner.Views.RecordingView">
<Grid>
<TextBlock Text="{Binding Header}" />
</Grid>
</UserControl>
Wrapping up, a code listing that should explain some strange numbers:
public class RecordingRibbonTabGroupData : AbstractRibbonTabGroupData
{
public ObservableCollection<string> Barcodes { get; private set; }
public RecordingRibbonTabGroupData()
{
this.Barcodes = new ObservableCollection<string>();
this.Barcodes.Add("76765535642");
this.Barcodes.Add("43435356");
}
}
Without DataTemplate:
WITH DataTemplate:
What you need is two ItemContainerStyle
<Ribbon:Ribbon ItemContainerStyle="{StaticResource RibbonTabStyle}" ItemsSource="{Binding DummyRibbonTabContent}">
first:
<Style TargetType="{x:Type Ribbon:RibbonTab}" x:Key="RibbonTabStyle">
<Setter Property="ItemsSource" Value="{Binding DummyRibbonGroups}" />
<Setter Property="ItemContainerStyle" Value="{DynamicResource RibbonGroupStyle}" />
<Setter Property="Header" Value="{Binding DummyRibbonHeader"} />
</Style>
second:
<Style TargetType="{x:Type Ribbon:RibbonGroup}" x:Key="RibbonGroupStyle">
<Setter Property="Header" Value="{Binding RibbonGroupHeader}" />
<Setter Property="ItemsSource" Value="{Binding DummyRibbonButtons}" />
<Setter Property="ItemTemplate" Value="{DynamicResource RibbonButtonTemplate}" />
</Style>
Obviously you have to create a ribbon button datatemplate. You could also use item templateselector for the ribbongroupstyle and then you can add not just ribbonbuttons but whatever you wish. Its not the exact solution you need, but I hope you get the idea.

Resources