I have a problem with a WPF business line application prototype that I do not know where to start to debug.
My Master Window (A) opens a "dialog" window using Dialog.Show(). The child window (B) has a combobox on it.
When the combobox is clicked (to expand and to show all the options) the entire second window (B) is hidden except the dropdownlist appearing from the combobox. The combobox isn't there, the window isn't there. Nothing is there except the dropdownlist and the master window behind it. If I click the master window then focus is once again set to window B and it shows as it should (because I set the Owner of window B to be window A).
To make it more interesting this bug is not consistent. It appears maybe 1 of 20 times I use the application, and when it starts appearing it can appear several times in a row and then stop happening again.
Possibly related is the fact that I think I have the same bug some times with MessageBoxes. When using MessageBox.Show() (Win forms msgbox) from a viewmodel when only the master window (A) is showing the box occationally appears behind the master window making it invisible for the user.
My application is using GalaSoft MvvmLight (if that could have anything to do with it) and quite a few telerik components. Other than that I am not sure what data to provide. I don't think anyone can tell me the source of the problem based on this information, but where would you start looking for the problem?
Update :
Good news! I have isolated the problem and found the combination of prerequisites for the phenomena to occur:
a) A component showing a PDF file is currently open anywhere withing the application.
b) A telerik component has been undocked and redocked.
I will include the code below, but I think the issue is buried deep within the RadDocking or WebBrowser component.
MainWindow
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OpenWindowClick(object sender, RoutedEventArgs e)
{
var w = new TestWindow { Owner = this, DataContext = new TestViewModel()};
w.Show();
}
}
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:telerikDocking="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Docking"
x:Class="TelerikGridDockingVsBrowserVsWindowBug.MainWindow"
Title="MainWindow" Height="750" Width="925">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="200"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<telerikDocking:RadDocking Grid.Row="0">
<telerikDocking:RadSplitContainer>
<telerikDocking:RadPaneGroup>
<telerikDocking:RadPane Header="I make bugs combined with a webbrowser showing a pdf document">
<TextBlock Text="1. Drag me and dock on the same or another location"/>
</telerikDocking:RadPane>
</telerikDocking:RadPaneGroup>
</telerikDocking:RadSplitContainer>
</telerikDocking:RadDocking>
<Button Grid.Row="1" Click="OpenWindowClick" Content="2. Open window" Height="50" Margin="0,20"/>
<WebBrowser Grid.Row="2" Source="http://www.kb.nl/sites/default/files/docs/pdf_guidelines.pdf"/>
</Grid>
TestWindow :
public partial class TestWindow : Window
{
public TestWindow()
{
InitializeComponent();
}
}
<Window x:Class="TelerikGridDockingVsBrowserVsWindowBug.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="150" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock> 3. Click on the combobox.</TextBlock>
<ComboBox Grid.Row="1" ItemsSource="{Binding Options}" SelectedItem="{Binding SelectedOption}" Height="50"></ComboBox>
<TextBlock Grid.Row="2"> This window will then disappear behind its owner</TextBlock>
</Grid>
TestWindow ViewModel:
public class TestViewModel
{
public List<string> Options { get; set; }
public string SelectedOption { get; set; }
public TestViewModel()
{
Options = new List<string> { "String1", "String2" };
SelectedOption = Options.First();
}
}
I have an open ticket with Telerik about this, so I will update here if I find out anything.
I have been in your situation before... you have an unexplained problem... it doesn't make any sense... you've tried everything that you can think of... in a last ditch effort to fix the problem, you throw it out to all the experienced users here... then nothing... no responses... no help.
The thing is that if you can't find and fix the problem with all of your code right in front of you, then how can we fix it without any code?
All I can suggest is for you to try to locate exactly where the problem originates. You can do this by commenting out sections of code and then running your solution to see if that has removed the problem or whether it still exists. The fact that your problem only occurs infrequently will make this stage even more tricky.
However, with perseverance, you should be able to narrow down where the problem arises. Once you have managed to exclude enough of your code, the last step is to see if you re-create it in a much smaller new WPF project. If you manage to get to this stage, please come back and show your reduced code... then, we may be able to help you further.
Related
I recentrly discovered "reusable controls" in WPF and I have a project where they seem to provide me with a solution to a problem I have.
Let me sketch the situation:
I need to make several UI elements. All of them share a common base, a common style/layout/template let's say, and they also share some common logic.
Next to that, all of these elements have some element-specific stuff.
You could say that I have some kind of inheritance here, but then for both XAML and CS.
The way I wanted to solve this, was by making an outer reusable element, I made a small example. The common part Is the Title label and the border. The element-specific UI can then be inserted into UserContent.
The code looks something like this (although it's simplified for the sake of brevity and conciseness, I also have an eventhandler and a routed event in my actual application):
ReusableControl.xaml
<UserControl x:Class="StackOverflowQuestion4.ReusableControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="root">
<Border BorderBrush="Black"
BorderThickness="1"
Width="400"
Height="200">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label Content="{Binding Title, ElementName=root}"
Grid.Row="0"/>
<ContentControl Content="{Binding UserContent, ElementName=root}"
Grid.Row="1"/>
</Grid>
</Border>
</UserControl>
ReusableControl.xaml.cs
using System.Windows;
using System.Windows.Controls;
namespace StackOverflowQuestion4
{
public partial class ReusableControl : UserControl
{
public ReusableControl()
{
InitializeComponent();
}
public string Title
{
get { return (string)GetValue(TitleProperty); }
set { SetValue(TitleProperty, value); }
}
public static readonly DependencyProperty TitleProperty =
DependencyProperty.Register("Title", typeof(string), typeof(ReusableControl), new PropertyMetadata(string.Empty));
public object UserContent
{
get { return GetValue(UserContentProperty); }
set { SetValue(UserContentProperty, value); }
}
public static readonly DependencyProperty UserContentProperty =
DependencyProperty.Register("UserContent", typeof(object), typeof(ReusableControl), new PropertyMetadata(string.Empty));
}
}
Lovely, I can now use my special control in other parts of my code, and I can insert whatever I want into the UserContent field.
MainWindow.xaml
<Window x:Class="StackOverflowQuestion4.MainWindow"
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:StackOverflowQuestion4"
mc:Ignorable="d"
Title="MainWindow"
SizeToContent="WidthAndHeight">
<Grid Width="800"
Height="600">
<local:ReusableControl Title="Test">
<local:ReusableControl.UserContent>
<Rectangle Width="300"
Height="100"
Fill="Blue"/>
</local:ReusableControl.UserContent>
</local:ReusableControl>
</Grid>
</Window>
This works great, but the problem arises when I start to name things. Simply adding a name to an element inside of my ReusableControl causes a compilation error.
<Rectangle Width="300"
Height="100"
Fill="Blue"
Name="LolWhatAmIDoing"/>
I get the following error:
MC3093 - Cannot set Name attribute value 'LolWhatAmIDoing' on element 'Rectangle'. 'Rectangle' is under the scope of element 'ReusableControl', which already had a name registered when it was defined in another scope.
This seems like such a small issue, but I cannot find an easy solution to this problem.
I found this thread on the forum, but it does not really provide a solution.
Since I'm pretty new to all of this, I also don't really get what the issue is, so apologies if I'm slow minded.
Should I move to CustomControls?
What you show is a simple property assignment: you set the value of type Rectangle to the property ReusableControl.UserContent. It's important to understand that the Rectangle is not part of the visual tree at this point. It's a simple property value that is only accessible via the property and not via the visual tree.
This all happens in the scope of MainWindow.
But the Rectangle is not a member of this scope. The ReusableControl is adding it to its own visual subtree or scope by binding the value of ReusableControl.UserContent to a ContentControl. This is were the Rectangle exists i.e. is rendered in the visual tree.
It effectively doesn't exist in the scope of MainWindow. It effectively only exists inside the ReusableControl in the "shape" of a ContentControl. This means that the scope of ReusableControl is the only name scope where you can register a name for child elements. It's also the only scope where you can directly reference it (if it had been defined and registered in this scope).
If you understand this, then you understand that the Rectangle is currently trying to register a name in the wrong scope, a scope in which it doesn't exist.
As a consequence, you cannot directly refer to it in the scope of MainWindow. You would have to dig into the ContentTemplate of the UserControl (which is a ContentControl) in order to get the nested ContentControl that actually hosts the Rectangle.
I was hoping someone would be kind enough to steer me in the right direction.
I’m trying to get my head around WPF.
I’ve created a 4.8 framework application with two buttons and a frame on the main window. I’m using Caliburn.Micro for binding – This is my test application to try and understand it.
When you press the first button, the window context is set to a page, with an Employees data grid upon it. The data is fed from a back end Oracle Database (I’m trying to follow the MVVM pattern.) This works perfectly.
When you press the other button, the main window displays a second page within the frame – This time, Departments. This much works. I have a an unbound textbox in the page and this shows as it should.
The part I have spent two days trying to get to work is the data grid for the Departments. It returns blank.
I know for certain that the Departments variable is being populated. I have confirmed this two ways. I have stepped through the code and can see values being populated. I have also placed the code below in the Employee page and had the data grid display with values.
I have also tried to display the employees data grid within the Departments page but it too is blank at run time.
To the best of my ability to discern, the code behind the two pages is identical aside from the obvious name change.
This is from my Departments View Model:
using Caliburn.Micro;
using DataLibrary;
namespace WpfEmployees.ViewModels
{
public class DepartmentsViewModel : Screen
{
public BindableCollection<DepartmentsModel> Departments { get; set; }
public DepartmentsViewModel()
{
DepartmentsProcessor dp = new DepartmentsProcessor();
Departments = new BindableCollection<DepartmentsModel>(dp.LoadDepartments());
}
}
}
From the view:
<DataGrid x:Name="Departments" ></DataGrid>
I'm not seeing any error messages, just a blank data grid.
Any suggestions of where I might look for my error would be very much appreciated.
To get this to work:
Main window:
<Window x:Class="WpfEmployees.MainWindow"
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:WpfEmployees"
xmlns:viewmodels="clr-namespace:WpfEmployees.ViewModels"
xmlns:views="clr-namespace:WpfEmployees.Views"
mc:Ignorable="d"
Title="MainWindow" Height="850" Width="650" Loaded="ShowDepartmentsPage">
<Window.Resources>
<DataTemplate x:Name="employeesViewTemplate" DataType="{x:Type viewmodels:EmployeesViewModel}">
<views:EmployeesView DataContext="{Binding}"/>
</DataTemplate
<DataTemplate x:Name="departmentsViewTemplate" DataType="{x:Type viewmodels:DepartmentsViewModel}">
<views:DepartmentsView DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
...
<Button x:Name="ShowEmployeesBtn" Content="Employees"
Margin="10 0 0 0" Click="ShowEmployeesPage"/>
<Button x:Name="ShowDepartmentsBtn" Content="Departments"
Margin="10 0 0 0" Click="ShowDepartmentsPage" />
...
<ContentControl Content="{Binding}"/>
</Grid>
</Window>
Main window class button code
public partial class MainWindow
{
...
private void ShowEmployeesPage(object sender, RoutedEventArgs e)
{
DataContext = new EmployeesViewModel();
}
View Model Code
public class EmployeesViewModel
...
public ObservableCollection<EmployeesModel> ObEmployees { get; set; }
...
public EmployeesViewModel()
{
EmployeesProcessor ep = new EmployeesProcessor();
ObEmployees = new ObservableCollection<EmployeesModel>
(ep.LoadEmployees());
}
I don't know if this is the best route. It works but I'm too new at WPF to know. I'm hoping this is what Ed Plunkett had in mind, so it may be. Thank you again, Ed.
I'm having some trouble with loading a view into a ContentControl. I'm trying to keep this as simple as possible so I used the Hello project that comes with CM. I made sure that the Hello project compiles correctly, and runs. It displays a window with a textbox, and a button. Both the textbox and button are wired at runtime to the sample ViewModel.
I modified the ShellView.xaml and replaced the StackPanel control with the Grid control, and setup the grid with 4 rows and a single column. I assigned the textbox to the first row, the button to the second row, and then two separate ContentControl to the final two rows.
<Grid Width="800" Height="600">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBox Grid.Row="0" Grid.Column="0" x:Name="Name" />
<Button Grid.Row="1" Grid.Column="0" x:Name="SayHello" Content="Click Me" />
<ContentControl Grid.Row="2" Grid.Column="0" x:Name="TopMenu"
VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"></ContentControl>
<ContentControl Grid.Row="3" Grid.Column="0" x:Name="BottomMenu"
VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"></ContentControl>
</Grid>
I created two separate C# classes in the ViewModels folder which are the ViewModels and are respectively called TopMenuViewModel.cs, and BottomMenuViewModel.cs. Both classes extend the PropertyChangedBase class. This is simply mimicking the ShellViewModel.cs class that comes with the sample project.
using System;
using Caliburn.Micro;
namespace TestWithCaliburnMicro.ViewModels
{
/// <summary>
/// Description of BottomMenuViewModel.
/// </summary>
public class BottomMenuViewModel : PropertyChangedBase
{
public BottomMenuViewModel()
{
}
}
I created two separate WPF User Controls in the Views folder which are the corresponding View and are respectively called TopMenuView.xaml and BottomMenuView.xaml. I added a Label in each xaml with the Content of "Top Menu" or "Bottom Menu" to differentiate between the two.
<Grid>
<Label>Bottom Menu View</Label>
</Grid>
In the ShellViewModel.cs class I created two public properties with only the "get" accessor set to return an instance of the corresponding ViewModel.
private BottomMenuViewModel _bottomMenu;
public BottomMenuViewModel BottomMenu {
get { return _bottomMenu; }
}
private TopMenuViewModel _topMenu;
public TopMenuViewModel TopMenu {
get { return _topMenu;}
}
Adding a break to the get accessor of either property shows that the get accessor is called when debugging the project. I added a simple statement to the constructor of the BottomMenuViewModel.cs class, such as int x = 0 and added a break to that line, but the break is never hit which to me means that the constructor is not called, so really the class is not created?
I believe what I'm doing is exceptionally basic and have read the All About Conventions document on the CM Codeplex site, and confirmed the logic with this comment: Prior question on stackoverflow
Hopefully someone will have the time to read this and point me in the right direction. Thanks.
Solution on GitHub. Note: made with SharpDevelop 4.x
GitHub solution
Either instantiate your view models in the constructor of the ShellViewModel, or if you wish to instantiate them at a later point, then add setters to your view model properties, and call the NotifyOfPropertyChange method to notify your UI that those property references have changed.
I would like to build a generic/re-usable modal dialog that I can use in our WPF (MVVM) - WCF LOB application.
I have a Views and associated ViewModels that I would like to display using dialogs. Bindings between Views and ViewModels are done using Type-targeted DataTemplates.
Here are some requirements that I have been able to draft:
I prefer this to be based on a Window instead of using Adorners and controls that act like a modal dialog.
It should get its minimum size from the content.
It should center on the owner window.
The window must not show the Minimize and Maximize buttons.
It should get its title from the content.
What is the best way to do this?
I usually deal with this by injecting this interface into the appropriate ViewModels:
public interface IWindow
{
void Close();
IWindow CreateChild(object viewModel);
void Show();
bool? ShowDialog();
}
This allows the ViewModels to spaw child windows and show them modally on modeless.
A reusable implementation of IWindow is this:
public class WindowAdapter : IWindow
{
private readonly Window wpfWindow;
public WindowAdapter(Window wpfWindow)
{
if (wpfWindow == null)
{
throw new ArgumentNullException("window");
}
this.wpfWindow = wpfWindow;
}
#region IWindow Members
public virtual void Close()
{
this.wpfWindow.Close();
}
public virtual IWindow CreateChild(object viewModel)
{
var cw = new ContentWindow();
cw.Owner = this.wpfWindow;
cw.DataContext = viewModel;
WindowAdapter.ConfigureBehavior(cw);
return new WindowAdapter(cw);
}
public virtual void Show()
{
this.wpfWindow.Show();
}
public virtual bool? ShowDialog()
{
return this.wpfWindow.ShowDialog();
}
#endregion
protected Window WpfWindow
{
get { return this.wpfWindow; }
}
private static void ConfigureBehavior(ContentWindow cw)
{
cw.WindowStartupLocation = WindowStartupLocation.CenterOwner;
cw.CommandBindings.Add(new CommandBinding(PresentationCommands.Accept, (sender, e) => cw.DialogResult = true));
}
}
You can use this Window as a reusable host window. There's no code-behind:
<Window x:Class="Ploeh.Samples.ProductManagement.WpfClient.ContentWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:self="clr-namespace:Ploeh.Samples.ProductManagement.WpfClient"
xmlns:pm="clr-namespace:Ploeh.Samples.ProductManagement.PresentationLogic.Wpf;assembly=Ploeh.Samples.ProductManagement.PresentationLogic.Wpf"
Title="{Binding Path=Title}"
Height="300"
Width="300"
MinHeight="300"
MinWidth="300" >
<Window.Resources>
<DataTemplate DataType="{x:Type pm:ProductEditorViewModel}">
<self:ProductEditorControl />
</DataTemplate>
</Window.Resources>
<ContentControl Content="{Binding}" />
</Window>
You can read more about this (as well as download the full code sample) in my book.
I'm answering my own question to help others find all answers I struggled to find in one place. What above seems like a straight forward problem, actually presents multiple problems that I hope to answer sufficiently below.
Here goes.
Your WPF window that will serve as the generic dialog can look something like this:
<Window x:Class="Example.ModalDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ex="clr-namespace:Example"
Title="{Binding Path=mDialogWindowTitle}"
ShowInTaskbar="False"
WindowStartupLocation="CenterOwner"
WindowStyle="SingleBorderWindow"
SizeToContent="WidthAndHeight"
ex:WindowCustomizer.CanMaximize="False"
ex:WindowCustomizer.CanMinimize="False"
>
<DockPanel Margin="3">
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" FlowDirection="RightToLeft">
<Button Content="Cancel" IsCancel="True" Margin="3"/>
<Button Content="OK" IsDefault="True" Margin="3" Click="Button_Click" />
</StackPanel>
<ContentPresenter Name="WindowContent" Content="{Binding}"/>
</DockPanel>
</Window>
Following MVVM, the right way to show a dialog is through a mediator. To use a mediator, you typically require some service locator as well. For mediator specific details, look here.
The solution I settled on involved implementing an IDialogService interface that is resolved through a simple static ServiceLocator. This excellent codeproject article has the details on that. Take note of this message in the article forum. This solution also solves the problem of discovering the owner window via the ViewModel instance.
Using this interface, you can call IDialogService.ShowDialog(ownerViewModel, dialogViewModel). For now, I'm calling this from the owner ViewModel, meaning I have hard references between my ViewModels. If you use aggregated events, you will probably call this from a conductor.
Setting the minimum size on the View that will eventually be displayed in the dialog doesn't automatically set the minimum size of the dialog. Also, since the logical tree in the dialog contains the ViewModel, you can't just bind to the WindowContent element's properties. This question has an answer with my solution.
The answer I mention above also includes code that centers the window on the owner.
Finally, disabling the minimize and maximize buttons is something WPF can't natively do. The most elegant solution IMHO is using this.
I have a LOT of MenuItem(s), and I want to be able to change their "Content" so that it displays in the program. When I load up the program, their "Content Name" is set in a Setter I created.. but the only problem is that I have almost a hundred MenuItem objects, and I need their display names in the program to be different (not the setter's default). I could just create over 100 different "Setter"'s and change one line in them.. but that is very time consuming. Is there a simpler approach? I want to be able to do this in the XAML where I am declaring them. Is there a way to do this? I've been searching and trying different attempts, but nothing so far.. perhaps someone knows?
EDIT:
Sorry, Perhaps I am being a bit unclear..
I already have created the MenuItems and they are based on the Setter that I have created... The problem is.. I now want each one to still be based on that Setter, but to have a unique "Content"/Name that displays for the user...Currently, they all have the "Content" name given to them by the setter, but I am looking for a way to set each MenuItem's content name through XAML.. is this possible?
Thanks
You question is not clear. i think the best way to create hundreds of menu items is to create them from the code not in XAML. for example in a foreach loop. then you can give each of them a unique and meaningfull name. please describe your problem more clearly.
thanks
Now I understand your problem. generaly i think it would be a very bad idea to set the content property for each of your menuItems in the XAML file. Specialy when you are dealing with hundreds of items. a better way is to use the Data binding feature of WPF and DataTemplates, not to hardcode the menuItem names in the XAML file. I will propuse two solutions for your problem. first solution uses the code-behind approach to create menu items and then bind them to MainMenu's ItemsSource property without using dataTemplates. the following code is the code-behind code for the window:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MenuItems = new ObservableCollection<MenuItem>();
for (int i = 0; i < 40; i++)
{
MenuItem menuItem = new MenuItem();
menuItem.Header = "MenuItem" + i.ToString();
MenuItems.Add(menuItem);
}
MainMenu.DataContext = this;
}
public ObservableCollection<MenuItem> MenuItems
{
get;
set;
}
}
in this code first we created 40 number of menuItems and then we bind them to the DataContext property of the MainMenu object. the following code shows the XAML code of the windows including it's MainMenu object:
<Window x:Class="WpfApplication17.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Menu Grid.Row="0" Name="MainMenu" ItemsSource="{Binding MenuItems}">
</Menu>
</Grid>
</Window>
in this approch you can first create all of your menu items and their names in the code and after that bind them to the Menu object. then you can use styles to set common properties of the menu items.
but a better solution is to use dataTemplates as I did in the following code. in this approach first you created a class to store your menu item names. then with the help of the data template feature of WPF you can bind them to your MainMenu items. the code-behind of this solution is as follows:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MenuItems = new ObservableCollection<CustomMenuItem>();
MenuItems.Add(new CustomMenuItem("Item 1"));
MenuItems.Add(new CustomMenuItem("Item 2"));
MenuItems.Add(new CustomMenuItem("Item 3"));
MainMenu.DataContext = this;
}
public ObservableCollection<CustomMenuItem> MenuItems
{
get;
set;
}
}
public class CustomMenuItem
{
public string Name { get; set; }
public CustomMenuItem(string name)
{
Name = name;
}
}
in this code I used the CustomMenuItem class to store menuitem names. the MainWindows constructor is respossible for creating the menuitems but you can retrieve them from other sources, like a XML file of database. the XAML code for the MainWindow is like this:
<Window x:Class="WpfApplication17.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication17"
Title="MainWindow" Height="350" Width="525" >
<Window.Resources>
<DataTemplate DataType="{x:Type local:CustomMenuItem}">
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Menu Grid.Row="0" Name="MainMenu" ItemsSource="{Binding MenuItems}">
</Menu>
</Grid>
</Window>
this way you can retrieve your menuitem names fot\r example from a XML file or from other data sources and they are not hardcoded into you XAML file. then you can use the powerfull features of DataTemplates to view you menu items the way you like.