Content Element bound to property - c#

I'm trying to have a window with a <Menu> element in it bound to a dependencyProperty:
Here is my Xaml:
<Window x:Class="attachement.xWindow"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Menu/>
<ToolBarTray x:Name="ToolBarTray" Grid.Row="1">
</ToolBarTray>
<ScrollViewer Grid.Row="2">
</ScrollViewer>
</Grid>
</Window>
and here is my code behind:
public partial class xWindow : Window
{
public Menu Menu
{
get { return (Menu)GetValue(MenuProperty); }
set { SetValue(MenuProperty, value); }
}
public static readonly DependencyProperty MenuProperty = DependencyProperty.Register("Menu", typeof(Menu), typeof(xWindow), new UIPropertyMetadata(0));
public xWindow()
{
InitializeComponent();
}
}
now my question is: how can I bind the <Menu> element in my xaml to the dependency property in code behind so that when I do "myXwindow.Menu = new Menu(){...};" the menu is updated in the window?
thanks
NB: I tried setting the xaml like this : <Menu x:Name="Menu"> and remove the dp in c# so taht I could access directly the Menu defined in xaml, wich seems to work (no build or run error) but does not allow me to set it anew after the window has been displayed

You can wrap your Menu in some other control
<ContentControl x:Name="_menuContainer">
<Menu/>
</ContentControl>
And then write your property like this:
public Menu Menu
{
get { return (Menu)_menuContainer.Content; }
set { _menuContainer.Content = value; }
}

Related

WPF Binding Paths

Someone please help me understand how my View is reaching my ViewModel.
My "BookView" has a Listview control that retrieves its data from a binding to a property in the "BookViewModel" using no namespaces. But the ViewModel is in a whole separate folder in the project so I wondering how it is finding it.
<UserControl x:Class="ContactBook.Views.BookView"
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:ContactBook.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Background="White">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListView Grid.Column="0"
ItemsSource="{Binding Path=ContactsVM.Contacts}"
SelectedItem="{Binding Path=ContactsVM.SelectedContact}">
</ListView>
</Grid>
</Grid>
</UserControl>
This is my view model with the ContactVM property in it.
public class BookViewModel : ObservableObject
{
public ContactsViewModel ContactsVM
{
get { return _contactsVM; }
set { OnPropertyChanged(ref _contactsVM, value); }
}
public BookViewModel()
{
}
}
These 2 files are in different folders in the project.
image

In a WPF application using Visual Studio, how can I bind a usercontrol to a usercontrol variable?

I'm not sure if I asked the question correctly, because I am not finding the answer by searching the internet. I am creating a wizard window. I have a window that has a title at the top and buttons at the bottom that will stay there throughout changing the pages. So in the xaml.cs for the window, I have a list of UserControls that will contain all of the views for the wizard. I also have a property/field that holds the current view. I want to create a xaml UserControl tag that binds to the current view property. It should change when I change the current view property (I have already implemented the INotifyChanged interface). The current view property will be changed by c#. Here is the code that I have(simplified to show whats needed), and when I run it nothing shows in the view area:
WizardWindow.xaml:
<Window x:Class="WizardWindow.WizardWindow"...>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="*"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Text="Wizard Title"/>
</Grid>
<Grid Grid.Row="1">
<Border Name="WizardWindowPageContent" Margin="5" BorderBrush="Black" BorderThickness="1">
<!--This is what I have tried but isn't working -->
<UserControl Content="{Binding CurrentView}" />
</Border>
</Grid>
<Grid Grid.Row="2" Name="WizardButtons">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0">Cancel</Button>
<Button Grid.Column="2">Back</Button>
<Button Grid.Column="3">Next</Button>
</Grid>
</Grid>
</Window>
WizardWindow.xaml.cs:
using System;
...
using System.Windows.Controls;
namespace WizardWindow
{
public partial class WizardWindow : Window, INotifyPropertyChanged
{
// List of views in the config window
private List<UserControl> views;
// Current view showing in the window
private UserControl currentView;
public UserControl CurrentView
{
get { return currentView; }
set
{
currentView = value;
OnPropertyChanged("CurrentView");
}
}
// Used to keep track of the view index
private int viewIndex;
public WizardWindow ()
{
InitializeComponent();
// Set the screen to the center of the screen
WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
views = new List<UserControl>();
views.Add(new FirstWizardPage(this));
viewIndex = 0;
CurrentView = views[viewIndex];
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
}
FirstWizardPage.xaml:
<!-- This should show up in the window -->
<UserControl x:Class="WizardWindow.FirstWizardPage" ... >
<Grid>
<TextBlock>Lorem Ipsum ...</TextBlock>
</Grid>
</UserControl>
FirstWizard.xaml.cs:
using System.Windows.Controls;
namespace WizardWindow
{
public partial class FirstWizardPage : UserControl
{
public FirstWizardPage(WizardWindow window)
{
InitializeComponent();
}
}
}
The given possible duplicate, Window vs Page vs UserControl for WPF navigation?
, is a good solution if I wanted to rewrite my program. However, it is not a solution to my exact problem. Someone else might have a similar problem and need this solution.
you need to use this:
<ContentControl x:Name="MyView"
Content="{Binding CurrentView}" />
It should work for you, but it's not MVVM.
You must binding on property DataContext of your user control, if you want to use MVVM.
I Will show little example for you:
It is MainWindow.xaml:
<Window x:Class="WpfApp2.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:WpfApp2"
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:Foo DataContext="{Binding UserControlViewModel}"/>
</Grid>
It is MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
It is MainWindowViewModel.cs:
public class MainWindowViewModel
{
public MainWindowViewModel()
{
UserControlViewModel = new UserControlViewModel{ Name = "Hello World" };
}
public UserControlViewModel UserControlViewModel { get; }
}
It is Foo.xaml:
<UserControl x:Class="WpfApp2.Foo"
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:WpfApp2"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="{Binding Name}"/>
</Grid>
It is FooUsercontrolViewModel.cs
public class FooUserControlViewModel
{
public string Name { get; set; }
}

How to design custom styled window in WPF?

I am new to WPF with mostly Winforms and Webforms experience. I am trying to learn WPF and one thing that I am trying to learn is creating beautiful UI in XAML. I have been trying to replicate the UI of StaffLynx application. The screen shots are present here
http://nextver.com/site/portfolio/stafflynx/
I cannot figure out in WPF, what will be the best way to create the placeholder container for the windows. In the link above you can see all the pages (views) are loaded in a custom shaped window. How can I create a re-usable window like this?
Should I just override the template of some control?
In short I am not sure what is the right way to create a custom shaped window such as the one used by StaffLynx app.
Please advise.
Maybe you should try using a ContentTemplateSelector. Here's a good example..
Here's a simple example that I made that may fit to your scenario. I have a window that has a border and inside the border is a ContentControl that has a template selector that will allow you to choose which view to display.
Here's the view:
Take a look at the local:MyContentTemplateSelector tag.
<Window x:Class="WpfApplication2.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:WpfApplication2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.Resources>
<DataTemplate x:Key="FirstTemplate">
<TextBlock Text="First" />
</DataTemplate>
<DataTemplate x:Key="SecondTemplate">
<TextBlock Text="Second" />
</DataTemplate>
<local:MyContentTemplateSelector FirstTemplate="{StaticResource FirstTemplate}" SecondTemplate="{StaticResource SecondTemplate}"
x:Key="mytemplateSelector" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border BorderThickness="1" BorderBrush="Red" Grid.Row="0">
<ContentControl ContentTemplateSelector="{StaticResource mytemplateSelector}" Content="{Binding SelectedViewModel}"/>
</Border>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1">
<Button Command="{Binding SelectFirstViewModel}">Go to First Template</Button>
<Button Command="{Binding SelectSecondViewModel}">Go to Second Template</Button>
</StackPanel>
</Grid>
</Window>
Here's the view model:
public class MainVm : ViewModelBase
{
private FirstVm _FirstViewModel;
public FirstVm FirstViewModel
{
get { return _FirstViewModel; }
set { Set(ref _FirstViewModel, value); }
}
private SecondVm _SecondViewModel;
public SecondVm SecondViewModel
{
get { return _SecondViewModel; }
set { Set(ref _SecondViewModel, value); }
}
private ViewModelBase _SelectedViewModel;
public ViewModelBase SelectedViewModel
{
get { return _SelectedViewModel; }
set { Set(ref _SelectedViewModel, value); }
}
public ICommand SelectFirstViewModel
{
get
{
return new RelayCommand(() => { this.SelectedViewModel = FirstViewModel; });
}
}
public ICommand SelectSecondViewModel
{
get
{
return new RelayCommand(() => { this.SelectedViewModel = SecondViewModel; });
}
}
public MainVm()
{
FirstViewModel = new FirstVm();
SecondViewModel = new SecondVm();
SelectedViewModel = this.FirstViewModel;
}
}
These can be any view model that you have for your pages:
public class FirstVm : ViewModelBase
{
}
public class SecondVm : ViewModelBase
{
}
And here's the template selector. This is the the important part. Whenever you change the content of you ContenControl, in this case the content is bound to the SelectedViewmodel property of the MainVm, the SelectTemplate method in this class will be called. that's where you put the logic on which view or data template display.
public class MyContentTemplateSelector : DataTemplateSelector
{
public DataTemplate FirstTemplate { get; set; }
public DataTemplate SecondTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is FirstVm)
return FirstTemplate;
if (item is SecondVm)
return SecondTemplate;
return null;
}
}
It will look like something like these:
Oh, ok if you just want one of many examples how to do that sort of thing. Here's a quick example of how to cut a corner like that using Clip, give it a shot. Hope it helps.
<Window x:Class="NestedCutCornerWindowCWSO"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NestedCutCornerWindowCWSO" Height="500" Width="800">
<Grid Height="350" Width="500">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Navy"
Clip="M0,0 L485,0 500,15 500,100 0,100 z"/>
<TextBlock Foreground="White" FontSize="20" Text="Something" Margin="5"/>
<Rectangle Grid.Row="1"
Fill="White"
Stroke="Navy" StrokeThickness="2"/>
<TextBlock Grid.Row="1" Foreground="Black" FontSize="30"
HorizontalAlignment="Center" VerticalAlignment="Center"
Text="Some Other Stuff..."/>
</Grid>
</Window>

Catch button click on user control

I have a window with a button and usercontrol. Now when I click on the button, the raised event, I wanna catch on usercontrol. I guess this principle it called event tunneling. But I don't know too, if this way would be the right way to catch the button click event.
Example
<Window x:Class="EventTunneling.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:EventTunneling"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Click me"></Button>
<uc:Control Grid.Row="1"></uc:Control>
</Grid>
</Window>
UserControl
<UserControl x:Class="EventTunneling.Control"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
</Grid>
</UserControl>
Now i want to catch the event on the partial class of usercontrol, that was raised from main window. How can i do that?
namespace EventTunneling
{
/// <summary>
/// Interaction logic for Control.xaml
/// </summary>
public partial class Control : UserControl
{
public Control()
{
InitializeComponent();
}
}
}
Unfortunately, you're doing this all wrong. First, you need to data bind your collection from the code behind to the DataGrid.ItemsSource property. Then, when the Button is clicked, just handle it in the code behind, where you have access to the collection and simply save it however you like:
In UserControl:
<DataGrid ItemsSource="{Binding Items}" ... />
In MainWindow:
<Button Grid.Row="0" Content="Click me" Click="Button_Click" />
In code behind (you should implement the INotifyPropertyChanged interface on this property):
public ObservableCollection<YourDataType> Items { get; set; }
...
private void Button_Click(object sender, RoutedEventArgs e)
{
SaveAsXml(Items);
}
In code behind constructor (or set it in any valid way you prefer):
DataContext = this;
Make a public event on the window.
Fire the event when the button is clicked.
Subscribe to the event from your control
you can simply do this when the button is clicked make a call to a public method in your user Control :
namespace EventTunneling
{
public partial class Control : UserControl
{
public Control()
{
InitializeComponent();
}
public void CatchEventOnUserControl()
{
//do your job here
//....
}
}
}
then when the button is clicked call the CatchEventOnUserControl method
<Window x:Class="EventTunneling.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:EventTunneling"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Button Grid.Row="0" Content="Click me" Click="Button_Click"></Button>
<uc:Control x:Name="mycontrol" Grid.Row="1"></uc:Control>
</Grid>
and the code behind is
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
mycontrol.CatchEventOnUserControl();
}
}

Can I specify a generic type in XAML (pre .NET 4 Framework)?

In XAML I can declare a DataTemplate so that the template is used whenever a specific type is displayed. For example, this DataTemplate will use a TextBlock to display the name of a customer:
<DataTemplate DataType="{x:Type my:Customer}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
I'm wondering if it's possible to define a DataTemplate that will be used any time an IList<Customer> is displayed. So if a ContentControl's Content is, say, an ObservableCollection<Customer> it would use that template.
Is it possible to declare a generic type like IList in XAML using the {x:Type} Markup Extension?
Not directly in XAML, however you could reference a DataTemplateSelector from XAML to choose the correct template.
public class CustomerTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
DataTemplate template = null;
if (item != null)
{
FrameworkElement element = container as FrameworkElement;
if (element != null)
{
string templateName = item is ObservableCollection<MyCustomer> ?
"MyCustomerTemplate" : "YourCustomerTemplate";
template = element.FindResource(templateName) as DataTemplate;
}
}
return template;
}
}
public class MyCustomer
{
public string CustomerName { get; set; }
}
public class YourCustomer
{
public string CustomerName { get; set; }
}
The resource dictionary:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
>
<DataTemplate x:Key="MyCustomerTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="150"/>
</Grid.RowDefinitions>
<TextBlock Text="My Customer Template"/>
<ListBox ItemsSource="{Binding}"
DisplayMemberPath="CustomerName"
Grid.Row="1"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="YourCustomerTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="150"/>
</Grid.RowDefinitions>
<TextBlock Text="Your Customer Template"/>
<ListBox ItemsSource="{Binding}"
DisplayMemberPath="CustomerName"
Grid.Row="1"/>
</Grid>
</DataTemplate>
</ResourceDictionary>
The window XAML:
<Window
x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Height="300"
Width="300"
xmlns:local="clr-namespace:WpfApplication1"
>
<Grid>
<Grid.Resources>
<local:CustomerTemplateSelector x:Key="templateSelector"/>
</Grid.Resources>
<ContentControl
Content="{Binding}"
ContentTemplateSelector="{StaticResource templateSelector}"
/>
</Grid>
</Window>
The window code behind:
public partial class Window1
{
public Window1()
{
InitializeComponent();
ObservableCollection<MyCustomer> myCustomers
= new ObservableCollection<MyCustomer>()
{
new MyCustomer(){CustomerName="Paul"},
new MyCustomer(){CustomerName="John"},
new MyCustomer(){CustomerName="Mary"}
};
ObservableCollection<YourCustomer> yourCustomers
= new ObservableCollection<YourCustomer>()
{
new YourCustomer(){CustomerName="Peter"},
new YourCustomer(){CustomerName="Chris"},
new YourCustomer(){CustomerName="Jan"}
};
//DataContext = myCustomers;
DataContext = yourCustomers;
}
}
Not out of the box, no; but there are enterprising developers out there who have done so.
Mike Hillberg at Microsoft played with it in this post, for example. Google has others of course.
You also can wrap your generic class in a derived class that specifies the T
public class StringList : List<String>{}
and use StringList from XAML.
aelij (the project coordinator for the WPF Contrib project) has another way to do it.
What's even cooler (even though it is sometime off in the future) ... is that XAML 2009 (XAML 2006 is the current version) is going to support this natively. Check out this PDC 2008 session for info on it and more.
Quite defeats the purpose of a generic, but you could define a class that derives from the generic like so, with the sole purpose of being able to use that type in XAML.
public class MyType : List<int> { }
And use it in xaml e.g. like
<DataTemplate DataType={x:Type myNamespace:MyType}>

Categories