Unfortunately, I'm unable to understand where there is realization mistake.
The main application window should show a content from view model (HomeView.xaml)
Could you advice, what I has done incorrect?
Thanks in advance.
https://i.stack.imgur.com/mq3PS.png
MainWindow.xaml.cs
using ohb.MVVM.ViewModel;
namespace ohb
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new HomeViewModel();
}
// Moveable window
private void WindowMouseMoving(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
this.DragMove();
}
}
// Exit button
private void CloseButtonClick(object sender, RoutedEventArgs e)
{
Close();
}
}
}
MainWindow.xaml
<Window x:Class="ohb.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:ohb"
xmlns:viewModel="clr-namespace:ohb.MVVM.ViewModel"
xmlns:view="clr-namespace:ohb.MVVM.View"
mc:Ignorable="d"
Height="600" Width="920"
WindowStyle="None"
ResizeMode="NoResize"
Background="Transparent"
AllowsTransparency="True"
MouseMove="WindowMouseMoving">
<Window.DataContext>
<viewModel:MainViewModel/>
</Window.DataContext>
<Border Background="#2f4f4f"
CornerRadius="30">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="Application"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Foreground="White"
FontSize="22"
Margin="20,0,0,0"/>
<StackPanel Grid.Row="1">
<RadioButton Content="Home"
Height="50"
Foreground="White"
FontSize="14"
Style="{StaticResource MenuButtonTheme}"
IsChecked="True"
Command="{Binding HomeViewCommand}"/>
<RadioButton Content="Menu"
Height="50"
Foreground="White"
FontSize="14"
Style="{StaticResource MenuButtonTheme}"
Command="{Binding DiscoveryViewCommand}"/>
<RadioButton Content="Settings"
Height="50"
Foreground="White"
FontSize="14"
Style="{StaticResource MenuButtonTheme}"/>
<RadioButton Content="Exit"
Click="CloseButtonClick"
Height="50"
Foreground="White"
FontSize="14"
Style="{StaticResource MenuButtonTheme}"/>
</StackPanel>
<TextBox Width="250"
Height="40"
VerticalContentAlignment="Center"
HorizontalAlignment="Left"
Margin="5"
Grid.Column="1"
Style="{StaticResource TextBoxTheme}"/>
<ContentControl Grid.Row="1"
Grid.Column="1"
Margin="10"
Content="{Binding CurrentView}"/>
</Grid>
</Border>
</Window>
App.xaml
<Application x:Class="ohb.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ohb"
xmlns:viewModel="clr-namespace:ohb.MVVM.ViewModel"
xmlns:view="clr-namespace:ohb.MVVM.View"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Theme/MenuButtonTheme.xaml"/>
<ResourceDictionary Source="Theme/TextBoxTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type viewModel:HomeViewModel}">
<view:HomeView/>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
MenuButtonTheme.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style BasedOn="{StaticResource {x:Type ToggleButton}}"
TargetType="{x:Type RadioButton}"
x:Key="MenuButtonTheme">
<Style.Setters>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="RadioButton">
<Grid VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="{TemplateBinding Background}">
<TextBlock Text="{TemplateBinding Property=Content}"
VerticalAlignment="Center"
Margin="50,0,0,0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="#3b6363"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
TextBoxTheme.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type TextBox}"
x:Key="TextBoxTheme">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border CornerRadius="10"
Background="#3b6363"
Width="200" Height="40">
<Grid>
<Rectangle StrokeThickness="1"/>
<TextBox Margin="1"
BorderThickness="0"
Background="Transparent"
VerticalContentAlignment="Center"
Padding="5"
Foreground="White"
x:Name="SearchBox"/>
<TextBlock IsHitTestVisible="False"
Text="Search"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
Foreground="DarkGray"
Grid.Column="1"
FontSize="12">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=SearchBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="Visibility" Value="Hidden"/>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
ObservableObject.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace ohb.Core
{
class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
if (PropertyChanged != null)
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
RelayCommand.cs
using System.Windows.Input;
namespace ohb.Core
{
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
}
MainViewModel.cs
using System;
using ohb.Core;
namespace ohb.MVVM.ViewModel
{
class MainViewModel : ObservableObject
{
public RelayCommand HomeViewCommand { get; set; }
public HomeViewModel HomeVM { get; set; }
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
HomeVM = new HomeViewModel();
CurrentView = HomeVM;
HomeViewCommand = new RelayCommand(o =>
{
CurrentView = HomeVM;
});
}
}
}
HomeViewModel.cs
using ohb.Core;
namespace ohb.MVVM.ViewModel
{
class HomeViewModel
{
}
}
HomeView.xaml
<UserControl x:Class="ohb.MVVM.View.HomeView"
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:viewModel="clr-namespace:ohb.MVVM.ViewModel"
xmlns:view="clr-namespace:ohb.MVVM.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<viewModel:HomeViewModel/>
</UserControl.DataContext>
<StackPanel>
<TextBlock Text="Discover"
Foreground="White"
FontSize="28"
HorizontalAlignment="Left"
Margin="0,0,0,20"/>
<StackPanel Orientation="Horizontal"
Margin="0,0,0,10">
<Border Width="400"
Height="200">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,2">
<GradientStop Color="#5bc3ff" Offset="0"/>
<GradientStop Color="#3aa0ff" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<Border.Clip>
<RectangleGeometry RadiusX="10"
RadiusY="10"
Rect="0,0,400,200"/>
</Border.Clip>
<Grid>
<StackPanel>
<TextBlock Text="World leader
in global"
Foreground="White"
FontSize="28"
Margin="20,10,10,0"/>
<TextBlock Text="Get started"
Foreground="White"
FontSize="18"
Margin="20,10,10,0"/>
</StackPanel>
</Grid>
</Border>
<Border Width="200"
Height="200"
CornerRadius="10"
Margin="45,0,0,0">
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,2">
<GradientStop Color="#5bc3ff" Offset="0"/>
<GradientStop Color="#3aa0ff" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
</Border>
</StackPanel>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="Margin" Value="15,0,0,0"/>
</Style>
</StackPanel.Resources>
<TextBlock Text="most watched"
Foreground="White"
FontSize="20"
HorizontalAlignment="Left"
Margin="0,0,0,10"/>
<StackPanel Orientation="Horizontal">
<Border Width="150"
Height="150"
Background="#844eff"
CornerRadius="10"
Margin="0"/>
<Border Width="150"
Height="150"
Background="Red"
CornerRadius="10"/>
<Border Width="150"
Height="150"
Background="Gray"
CornerRadius="10"/>
<Border Width="150"
Height="150"
Background="Green"
CornerRadius="10"/>
</StackPanel>
</StackPanel>
</StackPanel>
</UserControl>
A UserControl must not explictly set its own DataContext, as you have declared it in HomeView.xaml as
<UserControl.DataContext>
<viewModel:HomeViewModel/>
</UserControl.DataContext>
This assignment breaks the value inheritance of the DataContext property, which is essential to make the below DataTemplate work. Instead of getting the current data item as its DataContext, the control would always use the one that was explicitly set.
<DataTemplate DataType="{x:Type viewModel:HomeViewModel}">
<view:HomeView/>
</DataTemplate>
So simply remove the DataContext assignment from the UserControl's XAML.
Related
So I have a control like this simplified version:
<local:ImageMapField x:Class="ImageApp.WPF.Controls.ImageMapContentField"
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:ImageApp.WPF.Controls"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="Me">
<Grid HorizontalAlignment="Left">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" HorizontalAlignment="Stretch" Style="{DynamicResource BaseLabelStyle}">
<TextBlock Text="{Binding Header, RelativeSource={RelativeSource AncestorType=local:ImageMapContentField, Mode=FindAncestor}}" TextWrapping="WrapWithOverflow"></TextBlock>
</Label>
<StackPanel Grid.Column="1">
<Image />
<Border Margin="20,5,5,2">
<ContentPresenter Content="{Binding DataEntryContent, ElementName=Me}" />
</Border>
</StackPanel>
</Grid>
</local:ImageMapField>
And am using it like so:
<controls:ImageMapContentField Header="Foo Date"
FieldName="FooDate"
ImageSource="{Binding MyImage, Mode=TwoWay}"
ItemsSource="{Binding Map.Items}"
Zoom="{Binding MapFieldZoom}">
<controls:ImageMapContentField.DataEntryContent>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="{Binding MyDate, StringFormat=MM/dd/yyyy, ValidatesOnDataErrors=True, NotifyOnValidationError=True}">
<controls:WatermarkService.Watermark>
<TextBlock>Date</TextBlock>
</controls:WatermarkService.Watermark>
</TextBox>
<TextBox Grid.Column="1" Text="{Binding MyTime, StringFormat=HH\:mm}">
<controls:WatermarkService.Watermark>
<TextBlock>Time</TextBlock>
</controls:WatermarkService.Watermark>
</TextBox>
</Grid>
</controls:ImageMapContentField.DataEntryContent>
</controls:ImageMapContentField>
The problem is that because I am not binding my model's property to something on the ImageMapContentField, Validation.HasError on the ImageMapContentField is always false and never triggers.
What I get instead is the default TextBox validation.
What I really want is the ImageMapContentField to have a pink background. This works for my other controls where I am binding directly to something, but I cannot get this to work for the controls that have a ContentPresenter.
I am hoping I am just missing something that would allow the parent to capture the validation.
As requested here is a minimal example of the issue:
MainWindow.xaml
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="local:CustomTextField">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
<AdornedElementPlaceholder/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
<Setter Property="Background" Value="LightPink"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="local:CustomContentControl">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
<AdornedElementPlaceholder/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
<Setter Property="Background" Value="LightPink"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Window.DataContext>
<local:MyModel />
</Window.DataContext>
<StackPanel>
<local:CustomTextField LabelText="Number 1" Value="{Binding Number1, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True}" />
<local:CustomContentControl LabelText="Number 2">
<local:CustomContentControl.DataEntryContent>
<TextBox Text="{Binding Number2, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnNotifyDataErrors=True}" />
</local:CustomContentControl.DataEntryContent>
</local:CustomContentControl>
</StackPanel>
</Window>
CustomTextField.xaml
<UserControl x:Class="WpfApp1.CustomTextField"
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:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="Me">
<StackPanel>
<Label Content="{Binding ElementName=Me, Path=LabelText}" />
<TextBox Text="{Binding ElementName=Me, Path=Value}" />
</StackPanel>
</UserControl>
CustomTextField.cs
public partial class CustomTextField : UserControl
{
public static readonly DependencyProperty LabelTextProperty = DependencyProperty.Register(
"LabelText", typeof(string), typeof(CustomTextField), new PropertyMetadata(default(string)));
public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
"Value", typeof(string), typeof(CustomTextField), new PropertyMetadata(default(string)));
public string Value
{
get { return (string) GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}
public string LabelText
{
get { return (string) GetValue(LabelTextProperty); }
set { SetValue(LabelTextProperty, value); }
}
public CustomTextField()
{
InitializeComponent();
}
}
CustomContentControl.xaml
<UserControl x:Class="WpfApp1.CustomContentControl"
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:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
x:Name="Me">
<Grid>
<StackPanel>
<Label Content="{Binding ElementName=Me, Path=LabelText}" />
<ContentPresenter Content="{Binding DataEntryContent, ElementName=Me}" />
</StackPanel>
</Grid>
</UserControl>
CustomContentControl.cs
public partial class CustomContentControl : UserControl
{
public static readonly DependencyProperty LabelTextProperty = DependencyProperty.Register(
"LabelText", typeof(string), typeof(CustomContentControl), new PropertyMetadata(default(string)));
public static readonly DependencyProperty DataEntryContentProperty = DependencyProperty.Register(
"DataEntryContent", typeof(object), typeof(CustomContentControl), new PropertyMetadata(default(object)));
public object DataEntryContent
{
get { return (object) GetValue(DataEntryContentProperty); }
set { SetValue(DataEntryContentProperty, value); }
}
public string LabelText
{
get { return (string) GetValue(LabelTextProperty); }
set { SetValue(LabelTextProperty, value); }
}
public CustomContentControl()
{
InitializeComponent();
}
}
MyModel.cs
public class MyModel : INotifyPropertyChanged
{
int _number1;
int _number2;
public int Number1
{
get { return _number1; }
set
{
_number1 = value;
OnPropertyChanged();
}
}
public int Number2
{
get { return _number2; }
set
{
_number2 = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
The WPF validation is already bubbling up to the parent control (even when the child control is inside a ContentPresenter) - Validation.ErrorEvent
The problem here is that even though the event bubbles up, the attached property Validation.HasError doesn't get updated - that's basically due to the fact that there is no error in the control's property bindings. And hence, you don't see the background change.
To rectify this - you can use this code:
Update style in MainWindow.xaml
<Style TargetType="local:CustomContentControl">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
<AdornedElementPlaceholder/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="HasErrors" Value="True">
<Setter Property="Background" Value="LightPink"/>
</Trigger>
</Style.Triggers>
</Style>
And, update CustomContentControl to add a HasErrors dependency property, and validation error event handler
public static readonly DependencyProperty HasErrorsProperty = DependencyProperty.Register("HasErrors", typeof(bool), typeof(CustomContentControl), new PropertyMetadata(false));
public bool HasErrors
{
get { return (bool)GetValue(HasErrorsProperty); }
set { SetValue(HasErrorsProperty, value); }
}
public CustomContentControl()
{
InitializeComponent();
Validation.AddErrorHandler(this, (s, args) => {
if (args.Action == ValidationErrorEventAction.Added)
{
this.ToolTip = args.Error.ErrorContent;
HasErrors = true;
}
else
{
this.ToolTip = null;
HasErrors = false;
}
});
}
And your background will be updated.
I'm trying to develop a HamburgerMenu. a ContentControl that i restyled it.
my xaml code is something like this :
<Style TargetType="local:HamburgerMenu">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:HamburgerMenu">
<Grid x:Name="mainGrid" Background="{TemplateBinding Background}">
<!--HamburgerMenu button-->
<Border x:Name="border" Background="{TemplateBinding Background}" HorizontalAlignment="Left" VerticalAlignment="Top" Height="40" Width="50">
<ToggleButton x:Name="menuIcon" Background="Red" HorizontalAlignment="Left" VerticalAlignment="Top" Height="40" Width="50" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:HamburgerMenu}}, Path=IsOpen}">
<Path x:Name="path" HorizontalAlignment="Center" VerticalAlignment="Center" Stretch="Uniform" Width="30" Fill="{TemplateBinding MenuIconColor}" Data="M2,15.5L22,15.5 22,17.5 2,17.5 2,15.5z M2,10.5L22,10.5 22,12.5 2,12.5 2,10.5z M2,5.5L22,5.5 22,7.5 2,7.5 2,5.5z"/>
</ToggleButton>
</Border>
<!-- HamburgerMenu Items List-->
<ListBox x:Name="listbox" ItemsSource="{TemplateBinding Content}" HorizontalAlignment="Left" Margin="0,40,0,0" VerticalAlignment="Top" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectedIndex="0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="local:HamburgerMenuItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:HamburgerMenuItem">
<Button x:Name="ListBoxItemButton" Command="{TemplateBinding SelectionCommand}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<Grid x:Name="MainGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5" />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.ColumnSpan="2">
<Grid Background="Transparent" Margin="0" Width="300">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="45" />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="1">
<TextBlock Text="{TemplateBinding Text}" Margin="10,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Center" FontFamily="Segoe UI Light" FontSize="18" Foreground="{TemplateBinding Foreground}" TextWrapping="Wrap"/>
</Grid>
<Grid Grid.Column="0">
<Image Source="{TemplateBinding Icon}" Margin="10,5,5,5"/>
</Grid>
</Grid>
</Grid>
<Grid Name="ItemSelectedIndicator" Grid.Column="0" Background="{TemplateBinding SelectionIndicatorColor}" Visibility="Collapsed" />
</Grid>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and this is my HamburgerMenu classes :
public class HamburgerMenu : ContentControl
{
public new List<HamburgerMenuItem> Content
{
get { return (List<HamburgerMenuItem>)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public new static readonly DependencyProperty ContentProperty =
DependencyProperty.Register("Content", typeof(List<HamburgerMenuItem>), typeof(HamburgerMenu),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.AffectsMeasure));
static HamburgerMenu()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HamburgerMenu), new FrameworkPropertyMetadata(typeof(HamburgerMenu)));
}
public override void BeginInit()
{
Content = new List<HamburgerMenuItem>();
base.BeginInit();
}
}
public class HamburgerMenuItem : ListBoxItem
{
static HamburgerMenuItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HamburgerMenuItem), new FrameworkPropertyMetadata(typeof(HamburgerMenuItem)));
}
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(HamburgerMenuItem), new PropertyMetadata(String.Empty));
}
my question is how i can find witch ListBox item is selected in my window where i used my custom Control.
You could add a "SelectedItem" dependency property to your custom control and bind the SelectedItem property of the ListBox in the ControlTemplate to this one:
<!-- HamburgerMenu Items List-->
<ListBox x:Name="listbox" ItemsSource="{TemplateBinding Content}"
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}}"
HorizontalAlignment="Left" Margin="0,40,0,0" VerticalAlignment="Top" ScrollViewer.HorizontalScrollBarVisibility="Disabled" SelectedIndex="0"/>
You then bind your new dependency property to the source property in your window:
<local:HamburgerMenuItem ... SelectedItem="{Binding YourSourceProperty}" />
I Want to change a DependcyProperty(Named is as MessageTemplateProperty) which is in CustomControl and Type is Controltemplate .
But I can't set the value to it using datatrigger
But another DependcyProperty(named as IsShowMessageProperty) which is in CustomControl and Type is bool can be set using datatrigger.
Who can explain it ,Why, and How to solve it.
The Custom Code as follow:
public class MessageOverLay : ContentControl
{
static MessageOverLay()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MessageOverLay),
new FrameworkPropertyMetadata(typeof(MessageOverLay)));
}
#region DependcyProperties
// Template attached property
public static readonly DependencyProperty MessageTemplateProperty =
DependencyProperty.Register("MessageTemplate", typeof(ControlTemplate), typeof(MessageOverLay),
new FrameworkPropertyMetadata(MessageTemplateChanged));
public ControlTemplate MessageTemplate
{
set{SetValue(MessageTemplateProperty, value);}
get{return (ControlTemplate)GetValue(MessageTemplateProperty);}
}
// IsVisible attached property
public static readonly DependencyProperty IsShowMessageProperty =
DependencyProperty.Register("IsShowMessage", typeof(bool), typeof(MessageOverLay),
new PropertyMetadata(IsShowMessageChanged));
public bool IsShowMessage
{
get{return (bool)GetValue(IsShowMessageProperty);}
set{SetValue(IsShowMessageProperty, value);}
}
#endregion DependcyProperties
}
The Custom Default Theme Generic.xaml
<Style TargetType="{x:Type controls:MessageOverLay}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:MessageOverLay">
<AdornerDecorator>
<Grid>
<ContentPresenter x:Name="PART_Conent"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Content="{TemplateBinding Content}" />
<Control x:Name="Part_MessageControl" Template="{TemplateBinding MessageTemplate}"
Visibility="{TemplateBinding IsShowMessage,Converter={StaticResource BooleanToVisibilityConverter}}" />
</Grid>
</AdornerDecorator>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="MessageTemplate">
<Setter.Value>
<ControlTemplate>
<Grid HorizontalAlignment="Left" VerticalAlignment="Bottom">
<Grid Width="150" Height="100" Margin="5 0 0 10">
<Rectangle Stroke="Black" Fill="Yellow" RadiusX="6" RadiusY="6" Margin="0 20 0 0" />
<TextBlock Text="What are you doing?" Margin="5 25 0 0" />
<Button Content="Cancel" Margin="5" VerticalAlignment="Bottom" HorizontalAlignment="Right" />
<Button Content="OK" Margin="5" VerticalAlignment="Bottom" HorizontalAlignment="Left" />
</Grid>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I Want to use it as follow:
Demo XAML:
<Window ...>
<Window.Resources>
<ControlTemplate x:Key="GenderPopupTemplate">
<Grid HorizontalAlignment="Left" VerticalAlignment="Bottom">
<Grid Width="200" Height="100" Margin=" 5 0 0 10">
<TextBlock Text="Please Select Gender " Margin="5 25 0 0" />
<Button Content="Male" Margin="5" VerticalAlignment="Bottom" HorizontalAlignment="Left"
Command="{Binding SelectedGenderCommand}" />
<Button Content="FeMale" Margin="5" VerticalAlignment="Bottom" HorizontalAlignment="Right"
Command="{Binding SelectedGenderCommand}" />
</Grid>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="FacePopupTemplate">
<Grid HorizontalAlignment="Left" VerticalAlignment="Bottom">
<Grid Width="200" Height="100" Margin="5 10 0 0">
<TextBlock Text="Do you like your Face,Now?" Margin="5 25 0 0" />
<Button Content="OK" Margin="5" VerticalAlignment="Bottom" HorizontalAlignment="Left"
Command="{Binding OKCommand}" />
</Grid>
</Grid>
</ControlTemplate>
</Window.Resources>
<controls:MessageOverLay HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
MessageTemplate="{StaticResource GenderPopupTemplate}"
IsShowMessage="True">
<controls:MessageOverLay.Style>
<Style TargetType="{x:Type controls:MessageOverLay}">
<Style.Triggers>
<DataTrigger Binding="{Binding MessageBoxType}" Value="{x:Static viewModels:MessageBoxTypes.SelectView}">
<Setter Property="MessageTemplate" Value="{StaticResource GenderPopupTemplate}"></Setter>
<Setter Property="Height" Value="350"/>
</DataTrigger>
<DataTrigger Binding="{Binding MessageBoxType}" Value="{x:Static viewModels:MessageBoxTypes.MessageView}">
<Setter Property="MessageTemplate" Value="{StaticResource FacePopupTemplate}"></Setter>
<Setter Property="Height" Value="150"/>
</DataTrigger>
</Style.Triggers>
</Style>
</controls:MessageOverLay.Style>
<Grid Background="SeaGreen"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<TextBlock Text="Content"/>
</Grid>
</controls:MessageOverLay>
I want to change the MessageTemplate when the Propery MessageBoxType of MainWindowViewModel changed.
But I can't archive it.
The other related code
Demo C# Code:
public class MainWindowViewModel : BindableBase
{
public ICommand SelectedGenderCommand { get; }
public ICommand OKCommand { get; }
private MessageBoxTypes _messageBoxType;
public MessageBoxTypes MessageBoxType
{
get { return _messageBoxType; }
set { SetProperty(ref _messageBoxType, value); }
}
private string _title = "Prism Unity Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
MessageBoxType = MessageBoxTypes.SelectView;
SelectedGenderCommand = new DelegateCommand<object>(SelectedGender);
OKCommand = new DelegateCommand<object>(OK);
}
private void OK(object obj)
{
MessageBoxType = MessageBoxTypes.SelectView;
}
private void SelectedGender(object obj)
{
MessageBoxType = MessageBoxTypes.MessageView;
}
}
public enum MessageBoxTypes
{
SelectView,
MessageView,
ConfirmView
}
Update:
Here is the full demo code in github ,Please check it.
When a dependency property is to be set by a Style Setter, there must be no direct assignment of a so-called local value, as you do in
<controls:MessageOverLay MessageTemplate="{StaticResource GenderPopupTemplate}" ...>
The directly assigned local value always has higher precedence than any value from Style Setters (and other possible sources), so that the Setter has no effect. More details can be found here: Dependency Property Value Precedence.
Replace the direct assignment be another Style Setter:
<controls:MessageOverLay ...>
<controls:MessageOverLay.Style>
<Style TargetType="{x:Type controls:MessageOverLay}">
<Setter Property="MessageTemplate"
Value="{StaticResource GenderPopupTemplate}"/>
<Style.Triggers>
...
</Style.Triggers>
</Style>
</controls:MessageOverLay.Style>
...
</controls:MessageOverLay>
first the code :
MyStyle.xaml
<Style x:Key="MyStyle" TargetType="Button">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock x:Name="MyTextGen" Text="Foo"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
MyUserControl.xaml
<UserControl x:Name="Mycontrol">
<Grid>
<Button x:Name="Btn1" Style="{StaticResource MyStyle}"/>
<Button x:Name="Btn2" Style="{StaticResource MyStyle}"/>
</Grid>
</UserControl>
MyPage.xaml
<common:MainPage x:Name="MainPage">
<Grid>
<Popup x:Name="CatTool" IsOpen="False" HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="0 0 300 500">
<cat:MyControl >
<cat:MyContro.Transitions>
<TransitionCollection>
<PopupThemeTransition/>
</TransitionCollection>
</cat:MyContro.Transitions>
</cat:CatPagecontrol>
</Popup>
</Grid>
</common:MainPage>
Info.cs
MyDict = new Dictionary<string, string>
{
{"First", "MyFirst"},
{"Second", "MySecond")}
};
I would like to bind MyDict "First" and "Second" in MyTextGenby the MainPage, is it possible ?
MyStyle.xaml
<Style x:Key="MyStyle" TargetType="Button">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock x:Name="MyTextGen" Text="{TemplateBinding Content}"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
MyUserControl.xaml
<UserControl>
<Grid>
<ItemsControl ItemsSource="{Binding Path=Buttons}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{StaticResource MyStyle}" Content="{Binding}" Tag="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
MyUserControl.xaml.cs
public IEnumerable<string> Buttons {
get { return (IEnumerable<string>)GetValue(ButtonsProperty); }
set { SetValue(ButtonsProperty, value); }
}
public static readonly DependencyProperty ButtonsProperty =
DependencyProperty.Register("Buttons", typeof(IEnumerable<string>),
typeof(MyUserControl));
MainWindow.xaml
<Border>
<cat:MyUserControl Buttons="{Binding Buttons}" />
</Border>
MainWindow.xaml.cs
public IEnumerable<string> Buttons { get; set; }
private Dictionary<string, string> MyDict = new Dictionary<string, string> { { "First", "MyFirst" }, { "Second", "MySecond" } };
public MainWindow() {
this.DataContext = this;
Buttons = MyDict.Keys;
InitializeComponent();
}
I am developing windows phone 8.1 app and I need circular progressbar.
I have this usercontrol code:
<UserControl.Resources>
<Style TargetType="ProgressBar" x:Key="CircularProgressBarStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Grid x:Name="LayoutRoot">
<local:CircularProgressBarViewModel.Attach>
<local:CircularProgressBarViewModel HoleSizeFactor="0.75"/>
</local:CircularProgressBarViewModel.Attach>
<Ellipse Width="{Binding Diameter}" Height="{Binding Diameter}"
HorizontalAlignment="Center" VerticalAlignment="Center"
Stroke="LightGray" Opacity="0.5" Fill="Transparent"
StrokeThickness="10">
</Ellipse>
<local:PiePiece CentreX="{Binding CentreX}" CentreY="{Binding CentreY}"
RotationAngle="0" WedgeAngle="{Binding Angle}"
Radius="{Binding Radius}" InnerRadius="{Binding InnerRadius}"
Fill="Black" Opacity="0.7"/>
<Grid util:GridUtils.RowDefinitions="*,2*,*"
util:GridUtils.ColumnDefinitions="*,2*,*">
<Viewbox Grid.Row="1" Grid.Column="1">
<TextBlock Name="myValue" Text="{myValue value}"
Foreground="WhiteSmoke"
FontWeight="Bold"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Viewbox>
</Grid></Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
How can I change value with the name "myValue" from code behind (for example, from MainPage.xaml.cs) and not from CircularProgressBarViewModel?
If you need to get myValue from your MainPage, where you are using your UserControl you can create DependencyProperty in your control and set any value from page to control.
public sealed partial class YourUserControl: UserControl
{
public static readonly DependencyProperty myValueProperty = DependencyProperty.Register("myValue", typeof(string), typeof(YourUserControl), new PropertyMetadata(string.Empty));
public YourUserControl()
{
this.InitializeComponent();
}
public string myValue
{
get { return (string)GetValue(myValueProperty ); }
set { SetValue(myValueProperty , value); }
}
}
And on the your MainPage like this:
<controls:YourUserControl myValue={Binding YourValue}/>