Goal:
When user start typing text or characters in the textbox txtSearch the picture picEnlarger will be hidden and be replaced by picture picXmark.
In default, the picEnlarger will always display until input data will be applied in the textbox txtSearch. In order word, no data in textbox then display picEnlarger and hide picXmark.
Problem:
Having problem to display the picture picXmark and hide the picture picEnlarger when the user start typing characters in the textbox named txtSearch.
When I tried coding in C# to gain this functionality no effect would occur in the run time.
I tried using the code:
picEnlarger = new Image();
picXmark = new Image();
But no effect has happened.
XAML code from Stock.xaml:
<Canvas Height="39.667" Margin="8,0,215.397,0" VerticalAlignment="Top">
<Button x:Name="btnNewProduct" Content="New" Width="75" Click="btnNewProduct_Click" Height="20.277" RenderTransformOrigin="0.667,1.726" d:LayoutOverrides="VerticalAlignment, Margin" Canvas.Left="0.001" Canvas.Top="18.723" />
<Button x:Name="btnAddDelivery" Content="Add quantity" Width="75" Click="btnAddDelivery_Click" d:LayoutOverrides="VerticalAlignment, Margin" Height="20.277" Canvas.Left="79.001" Canvas.Top="18.723" />
<Button x:Name="btnDeleteProduct" Content="Delete" Width="75" RenderTransformOrigin="0.107,1.843" Click="btnDeleteProduct_Click" Height="20.277" Canvas.Left="158.001" d:LayoutOverrides="HorizontalAlignment, VerticalAlignment, Width" Canvas.Top="18.723" />
<Button x:Name="btnEdit" Content="Edit" Canvas.Left="237.001" Width="75" Canvas.Top="18.723" Click="btnEdit_Click" />
<TextBox Name="txtSearch" Canvas.Left="391.36" TextWrapping="Wrap" Canvas.Top="18.723" Width="143.243" TextChanged="txtSearch_TextChanged" Text=" Search article" PreviewMouseLeftButtonDown="txtSearch_PreviewMouseLeftButtonDown" TextInput="txtSearch_TextInput">
</TextBox>
<Label Content="Advanced Search" HorizontalAlignment="Left" Canvas.Left="444.289"/>
<Image x:Name="picXmark" Height="8" Source="/MediaStore;component/Bilder/search_xmark.gif" Stretch="Fill" Width="8" Canvas.Left="519.853" Canvas.Top="24.167" Visibility="Hidden" />
<Image x:Name="picEnlarger" Height="14" Canvas.Left="513.75" Source="/MediaStore;component/Bilder/search_enlarger2.gif" Stretch="Fill" Canvas.Top="21.527" Width="14" Visibility="Hidden" ImageFailed="picEnlarger_ImageFailed" />
</Canvas>
Class Stock
private void txtSearch_TextChanged(object sender, TextChangedEventArgs e)
{
picEnlarger = new Image();
picXmark = new Image();
if (txtSearch.Text != "")
{
picEnlarger.Visibility = Visibility.Collapsed;
picXmark.Visibility = Visibility.Visible;
RegularSearch myRegularSearch = new RegularSearch();
myRegularSearch.Test(txtSearch.Text);
}
else
{
picEnlarger.Visibility = Visibility.Visible;
picXmark.Visibility = Visibility.Hidden;
}
}
private void txtSearch_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
txtSearch.Text = "";
}
In theory you should be able to just use triggers for that, e.g.
<TextBox Name="txtSearch" />
<Image Name="ImageOne">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Visibility" Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=txtSearch}"
Value="">
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
<Image Name="ImageOne">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=txtSearch}"
Value="">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
When text is entered one image will become visible while the other one will be hidden.
Related
I have a WPF application where I have view which lets User choose list of fields at run-time and I am storing in a Text file and I am trying to create a Data entry form based on the list of fields that user created Run-time.
I have developed an solution using code behind but I am trying to implement this using MVVM.
Approach 1: I can create the textBlock and Textbox in the code-behind and bind it to the properties in the Viewmodel. The viewmodel will have all the possible field property.
<TabControl Margin="335,10,10,71" TabStripPlacement="Bottom">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Grid>
<Border
Name="Border"
Margin="0,0,0,0"
Background="Transparent"
BorderBrush="Black"
BorderThickness="1,1,1,1"
CornerRadius="5">
<ContentPresenter
x:Name="ContentSite"
Margin="12,2,12,2"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Header"
RecognizesAccessKey="True">
<ContentPresenter.LayoutTransform>
<RotateTransform Angle="0" />
</ContentPresenter.LayoutTransform>
</ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Height" Value="40" />
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Panel.ZIndex" Value="200" />
<Setter TargetName="Border" Property="Background" Value="Black" />
<Setter TargetName="Border" Property="BorderBrush" Value="White" />
<Setter TargetName="Border" Property="BorderThickness" Value="1,1,1,0" />
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="FontSize" Value="20" />
<Setter Property="FontWeight" Value="Bold" />
<Setter TargetName="Border" Property="Background" Value="Black" />
<Setter TargetName="Border" Property="BorderBrush" Value="White" />
<Setter Property="Foreground" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TabControl.Resources>
<TabItem Header="Product Data 1">
<Grid x:Name="Grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="Column_1" Width="*" />
<ColumnDefinition x:Name="Column_2" Width="*" />
</Grid.ColumnDefinitions>
<Border
Margin="0,0,0,10"
Background="LightSkyBlue"
BorderBrush="Gray"
BorderThickness="2,2,2,2">
<StackPanel
x:Name="MainStack"
Margin="20,0,0,0"
x:FieldModifier="public"
Grid.IsSharedSizeScope="True"
Orientation="Vertical" />
</Border>
<Border
Grid.Column="1"
Margin="0,0,0,10"
Background="LightSkyBlue"
BorderBrush="Gray"
BorderThickness="2,2,2,2">
<StackPanel
x:Name="SecondStack"
Grid.Column="1"
Margin="20,0,0,0"
x:FieldModifier="public"
Grid.IsSharedSizeScope="True"
Orientation="Vertical" />
</Border>
<!--<StackPanel x:Name="ThirdStack" x:FieldModifier="public" Grid.Column="2" Orientation="Vertical" Grid.IsSharedSizeScope="True" Width="auto" Height="476" VerticalAlignment="Center" HorizontalAlignment="Center" />
<Border BorderBrush="Black" BorderThickness="1,1,1,1" Grid.Column="2" Margin="0,0,0,10"/>-->
</Grid>
</TabItem>
</TabControl>
Code-Behind=
string[] Data = File.ReadAllLines("Field.txt");
foreach (var part in Data)
{
Grid mytxtBStack = new Grid();
MainStack.Children.Add(mytxtBStack);
ColumnDefinition column_1 = new ColumnDefinition();
column_1.SharedSizeGroup = "FirstColumn";
ColumnDefinition column_2 = new ColumnDefinition();
column_2.SharedSizeGroup = "SecondColumn";
mytxtBStack.ColumnDefinitions.Add(column_1);
mytxtBStack.ColumnDefinitions.Add(column_2);
txtBlock = new TextBlock();
txtBlock.Name = "MyBlock" + i;
txtBlock.Text = part;
txtBlock.FontSize = 14;
txtBlock.FontWeight = FontWeights.DemiBold;
txtBlock.Foreground = Brushes.Black;
txtBlock.Margin = new Thickness(0, 20, 0, 0);
mytxtBStack.Children.Add(txtBlock);
txtBox = new TextBox();
txtBox.Name = "MyText" + i.ToString();
txtBox.FontSize = 12;
txtBox.Height = 25;
txtBox.Width = 250;
txtBox.BorderThickness = new Thickness(1, 1, 1, 1);
txtBox.Margin = new Thickness(130, 20, 0, 0);
txtBox.Foreground = Brushes.Black;
txtBox.BorderBrush = Brushes.Black;
txtBox.Background = Brushes.White;
txtList.Add(txtBox);
mytxtBStack.Children.Add(txtBox);
}
Approach 2: I can create a dynamic class using the Expandoobject based on the Textfile and create a itemscontrols to bind with view and mentioned all the possibile field properties in the Viewmodel.
I am thinking about implementing this part with
inside the TabControl,
<ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Fields">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}" />
<TextBox Width="300" Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Can anyone please suggest me a way to do this with MVVM wpf?
I implemented this using the CommunityToolkit.Mvvm NuGet package.
MainWindowViewModel.cs
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
namespace WpfApp1;
public class Field
{
public string Block { get; set; } = string.Empty;
public string Text { get; set; } = string.Empty;
}
public partial class MainWindowViewModel : ObservableObject
{
[ObservableProperty]
// CommunityToolkit's source generator will create a "Field" property.
private ObservableCollection<Field> fields = new();
[RelayCommand]
// CommunityToolkit's source generator will create a "LoadFieldsCommand" command.
private void LoadFields()
{
for (int i = 0; i < 10; i++)
{
Fields.Add(new Field() { Block = $"Block#{i + 1}", Text = $"Text{i + 1}" });
}
}
[ObservableProperty]
// Bind this to the Text property.
private string textBlockText = string.Empty;
[ObservableProperty]
// Bind this to the ItemsSource property.
private List<string> comboBoxItems = new()
{
"Item A",
"Item B",
"Item C",
};
}
MainWindow.xaml.cs
using System.Windows;
namespace WpfApp1;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MainWindowViewModel ViewModel { get; } = new();
}
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:local="clr-namespace:WpfApp1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="ThisWindow"
Title="MainWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Command="{Binding ElementName=ThisWindow, Path=ViewModel.LoadFieldsCommand}"
Content="Load fields" />
<ItemsControl Grid.Row="1" ItemsSource="{Binding ElementName=ThisWindow, Path=ViewModel.Fields}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:Field">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Block}" />
<TextBox Text="{Binding Text}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
I'm making an application using C# and WPF
I have 8 toggle buttons.
When I click one of the buttons others should be disabled so I can't click it except one is activated.
When I click this button again others should be enabled
Styles.xaml:
<!--Toggle Button-->
<Style x:Key="ToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#535353"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="MinHeight" Value="30"/>
<Setter Property="Grid.Column" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="{TemplateBinding Background}"
BorderThickness="0">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml:
<ToggleButton x:Name="CategoryToggle1"
Grid.Row="1"
Style="{StaticResource ToggleButton}"
Checked="ShowOptions"
Unchecked="HideOptions" />
How Can I achieve this by code and XAML?
I want to do it like in this video:
Video
Thank you for comments guys, I found another solution.
MainWindow.XAML:
<RadioButton x:Name="CategoryToggle1"
Grid.Row="1"
Grid.Column="1"
Style="{StaticResource RadioButton}"
GroupName="ToggleButtonsGroup"
Checked="OpenOptions"
Unchecked="HideOptions"/>
Styles.xaml:
<!--Radio Button-->
<Style TargetType="RadioButton"
x:Key="RadioButton"
BasedOn="{StaticResource {x:Type ToggleButton}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ToggleButton IsChecked="{Binding IsChecked, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!--Toggle Button-->
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Background" Value="#535353" />
<Setter Property="MinHeight" Value="30" />
<Setter Property="Grid.Column" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Background="{TemplateBinding Background}"
BorderThickness="0">
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
<Trigger Property="IsEnabled" Value="true">
<Setter Property="Opacity" Value="1" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.cs:
private void OpenOptions(object sender, RoutedEventArgs e){
RadioButton radioButton = sender as RadioButton;
radioButton.IsChecked = true;
//Disable all option buttons except one that active
MyGrid.Children.OfType<RadioButton>().Where(rb => rb != radioButton &&
rb.GroupName == radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = false);
}
private void HideOptions(object sender, RoutedEventArgs e)
{
RadioButton radioButton = sender as RadioButton;
MyGrid.Children.OfType<RadioButton>().Where(rb => rb.GroupName ==
radioButton.GroupName).ToList().ForEach(rb => rb.IsEnabled = true);
}
Using Click events of each ToggleButton
One way you could do it is by giving a name to all your ToggleButtons, hook-up to their Click event and manually uncheck others in the code-behind:
XAML
<StackPanel Orientation="Vertical">
<StackPanel.Resources>
<Style TargetType="ToggleButton">
<Style.Triggers>
<Trigger Property="IsChecked" Value="False">
<Setter Property="MaxWidth" Value="15"/>
</Trigger>
</Style.Triggers>
</Style>
</StackPanel.Resources>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button1" Content="Button 1" Click="button1_Click"/>
<TextBlock Text="Line 1"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button2" Content="Button 2" Click="button2_Click"/>
<TextBlock Text="Line 2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button3" Content="Button 3" Click="button3_Click"/>
<TextBlock Text="Line 3"/>
</StackPanel>
</StackPanel>
Code-behind
private void button1_Click(object sender, RoutedEventArgs e) {
if (button1.IsChecked == true) {
button2.IsChecked = false;
button3.IsChecked = false;
}
}
private void button2_Click(object sender, RoutedEventArgs e) {
if (button2.IsChecked == true) {
button1.IsChecked = false;
button3.IsChecked = false;
}
}
private void button3_Click(object sender, RoutedEventArgs e) {
if (button3.IsChecked == true) {
button1.IsChecked = false;
button2.IsChecked = false;
}
}
This method is tedious, error-prone, requires code-behind and is not very scalable.
Binding IsChecked properties to a collection of bool with one true at a time.
Another way you could go (still by using code-behind) is to define a collection of boolean values and bind each ToggleButton.IsChecked on one of the bool in the collection, and ensure that the collection only contains at most one true at a time:
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button1" Content="Button 1" IsChecked="{Binding [0]}"/>
<TextBlock Text="Line 1"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button2" Content="Button 2" IsChecked="{Binding [1]}"/>
<TextBlock Text="Line 2"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="button3" Content="Button 3" IsChecked="{Binding [2]}"/>
<TextBlock Text="Line 3"/>
</StackPanel>
Code-behind
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
ObservableCollection<bool> states = new ObservableCollection<bool> { false, false, false };
states.CollectionChanged += States_CollectionChanged;
DataContext = states;
}
private void States_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) {
var collection = sender as ObservableCollection<bool>;
if (e.Action == NotifyCollectionChangedAction.Replace) {
if ((bool)e.NewItems[0]) {
for (int i = 0; i < collection.Count; i++) {
if (e.NewStartingIndex != i) {
collection[i] = false;
}
}
}
}
}
}
Again, this uses code-behind and not the view model but at least it is easier to add rows.
The behavior you need is very specific.
I don't know how, but i got here trying to make a toggle button behave like a radio button. Your answer was enlightning.
For what it's worth, here's how you would do that :
Resource :
<Style x:Key='RadioToggle' TargetType='RadioButton'
BasedOn='{StaticResource {x:Type ToggleButton}}' />
Control :
<RadioButton Content='RadioToggle1' IsChecked='True'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle2'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
<RadioButton Content='RadioToggle3'
Style='{StaticResource RadioToggle}'
GroupName="RadioToggleGroup" />
I would to like to enable a Button for a specific row of a DataGrid and disable it for the other rows in code behind. The thing is the IsEnabled property impact all the buttons in all the rows which is not what I want. It cannot have two different values (visible for some rows and Collapsed for others in the same time)
Can you help me please ?
here is my xaml :
<DataGrid ItemsSource="{Binding TaskUsers}" Name="DgUser" AutoGenerateColumns="False" IsReadOnly="True" SelectionUnit="FullRow" SelectionMode="Single" CanUserAddRows="False" CanUserSortColumns="False" SelectedValuePath="TaskUserId" >
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<Button BorderThickness="0" ToolTip="Subscribe" Click="AddTaskUser_OnClick">
<Image Source="/Sinergi;component/Images/add-icon16.png"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button BorderThickness="0" ToolTip="Delete a User" Click="DeleteTaskUser_OnClick" Name="DeleteUserButton" Loaded="DeleteUserButton_OnLoaded">
<Image Source="/Sinergi;component/Images/Trash-can-icon16.png" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="UserName" Binding="{Binding User.Name}" />
</DataGrid.Columns>
here is my code behind code
private void DeleteUserButton_OnLoaded(object sender, RoutedEventArgs e)
{
var delete = sender as Button;
if (delete == null) return;
foreach (var item in DgUser.Items)
{
var o = (TaskUser) item;
if (o.UserId == Identity.This.OnBehalfOfUser.UserId) continue;
delete.IsEnabled = false;
delete.Visibility = Visibility.Collapsed;
}
}
You should bind the IsEnabled property of the Button to a property of your TaskUser object and get rid of the DeleteUserButton_OnLoaded event handler:
<DataTemplate>
<Button BorderThickness="0" ToolTip="Delete a User" Click="DeleteTaskUser_OnClick" Name="DeleteUserButton">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding UserId}" Value="{x:Static local:Identity.This.OnBehalfOfUser.UserId}">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Content>
<Image Source="/Sinergi;component/Images/Trash-can-icon16.png" />
</Button.Content>
</Button>
</DataTemplate>
You need to set the Value of the DataTrigger to whatever Identity.This.OnBehalfOfUser.UserId is, or use a value converter:
<DataTemplate>
<DataTemplate.Resources>
<local:Converter x:Key="converter" />
</DataTemplate.Resources>
<Button BorderThickness="0" ToolTip="Delete a User" Click="DeleteTaskUser_OnClick" Name="DeleteUserButton">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding UserId, Converter={StaticResource converter}}" Value="False">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Content>
<Image Source="/Sinergi;component/Images/Trash-can-icon16.png" />
</Button.Content>
</Button>
</DataTemplate>
Converter:
class Converter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value == Identity.This.OnBehalfOfUser.UserId;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Sure Im missing something simple but I have overridden a checkbox as a user control so I can use different images as its bullet decorators. Seems to work ok but doesnt respond to any click or checked events. I need it to display the bound popup upon the click evnt but not change the bullet decorator images.
Also my binding seems to be broken as when the click even fires (once it works) it should present a popup with the bound data. WarningList is my data to bind and when debugging it shows its populated correctly, just not bound correctly I believe
<UserControl x:Class="Dev.App.Views.Controls.StatusResultCheckBox"
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:converters="clr-namespace:Dev.App.Views.Converters"
x:Name="Root"
d:DesignHeight="50"
d:DesignWidth="50"
mc:Ignorable="d">
<UserControl.Resources>
<converters:WarningListFilterByOperationTypeConverter x:Key="WarningListFilterByOperationTypeConverter" />
<DataTemplate x:Key="StatusResultNone">
<Viewbox Width="20" Height="20">
<Grid/>
</Viewbox>
</DataTemplate>
<DataTemplate x:Key="StatusResultWarning">
<BulletDecorator Background="Transparent">
<BulletDecorator.Bullet>
<Viewbox Width="20" Height="20">
<Grid>
<Path Data="F1M874.094,289.369L854.3,254.63C854.028,254.151 853.515,253.856 852.958,253.856 852.403,253.856 851.89,254.151 851.617,254.63L831.824,289.369C831.555,289.84 831.559,290.416 831.835,290.883 832.111,291.348 832.618,291.634 833.165,291.634L872.752,291.634C873.299,291.634 873.805,291.348 874.081,290.883 874.357,290.416 874.361,289.84 874.094,289.369z"
Stretch="Uniform" Fill="#FFFCCE00" Width="20" Height="20" Margin="0,0,0,0" RenderTransformOrigin="0.5,0.5" StrokeThickness="0.5">
</Path>
<Path Data="M855.653,287.189L850.264,287.189 850.264,282.745 855.653,282.745 855.653,287.189z M855.653,279.41L850.264,279.41 850.264,266.077 855.653,266.077 855.653,279.41z"
Stretch="Uniform" Fill="Black" Width="3" Height="10" Margin="0.5,3,0,0" RenderTransformOrigin="0.5,0.5" StrokeThickness="0.5">
</Path>
</Grid>
</Viewbox>
</BulletDecorator.Bullet>
<Popup Name="WarningMessagePopup"
Width="{Binding ElementName=ListBox, Path=ActualWidth}"
Height="200"
HorizontalAlignment="Center"
HorizontalOffset="50"
PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
IsOpen="{Binding Path=IsChecked}"
StaysOpen="False"
VerticalOffset="10">
<TextBox VerticalScrollBarVisibility="Auto">
<TextBox.Text>
<MultiBinding Converter="{StaticResource WarningListFilterByOperationTypeConverter}" Mode="OneWay">
<Binding Path="OperationType" />
<Binding Path="DataContext.WarningList" RelativeSource="{RelativeSource AncestorType=ListBox}" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</Popup>
</BulletDecorator>
</DataTemplate>
<ControlTemplate x:Key="CheckBoxTemplate" TargetType="ContentControl">
<ContentControl Name="Content" />
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding StatusResultCheckMode, ElementName=Root}" Value="None">
<Setter TargetName="Content" Property="ContentTemplate" Value="{StaticResource StatusResultNone}" />
</DataTrigger>
<DataTrigger Binding="{Binding StatusResultCheckMode, ElementName=Root}" Value="Warning">
<Setter TargetName="Content" Property="ContentTemplate" Value="{StaticResource StatusResultWarning}" />
<Setter Property="Cursor" Value="Hand" />
</DataTrigger>
<DataTrigger Binding="{Binding HasStatus, ElementName=Root}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="StatusResultVisibilityStyle" TargetType="CheckBox">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<BulletDecorator Background="Transparent">
<BulletDecorator.Bullet>
<Viewbox Width="20" Height="20">
<Grid />
</Viewbox>
</BulletDecorator.Bullet>
</BulletDecorator>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsInStatusNone.Value}" Value="False" />
<Condition Binding="{Binding IsInStatusWarning.Value}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Visibility" Value="Visible" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<CheckBox HorizontalAlignment="Center"
VerticalAlignment="Center"
IsChecked="True"
Style="{StaticResource StatusResultVisibilityStyle}"/>
<ContentControl Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Width="20" Height="20"
Template="{StaticResource CheckBoxTemplate}" />
</Grid>
</UserControl>
Called from my main page as follows. (nested inside a Listbox)
<controls:StatusResultCheckBox Grid.Column="3"
IsInStatusWarning="{Binding Path=IsInStatusWarning.Value}"
IsInStatusNone="{Binding Path=IsInStatusNone.Value}"
HasStatus="{Binding Path=HasStatus.Value}"
IsRunning="{Binding RelativeSource={RelativeSource AncestorType=ListBox}, Path=DataContext.IsRunning.Value}"/>
The code behind the user control...
using System.Windows;
using System.Windows.Controls;
namespace Dev.App.Views.Controls
{
/// <summary>
/// Interaction logic for StatusResultCheckBox.xaml
/// </summary>
public partial class StatusResultCheckBox : UserControl
{
public static readonly DependencyProperty StatusResultCheckModeProperty =
DependencyProperty.Register("StatusResultCheckMode", typeof(StatusResultCheckMode), typeof(StatusResultCheckBox), new PropertyMetadata(StatusResultCheckMode.None));
public static readonly DependencyProperty IsRunningProperty =
DependencyProperty.Register("IsRunning", typeof(bool), typeof(StatusResultCheckBox), new PropertyMetadata(false, IsRunningPropertyChanged));
public static readonly DependencyProperty HasStatusProperty =
DependencyProperty.Register("HasStatus", typeof(bool), typeof(StatusResultCheckBox), new PropertyMetadata(false, IsRunningPropertyChanged));
public static readonly DependencyProperty IsInStatusWarningProperty =
DependencyProperty.Register("IsInStatusWarning", typeof(bool), typeof(StatusResultCheckBox), new PropertyMetadata(false, IsRunningPropertyChanged));
public static readonly DependencyProperty IsInStatusNoneProperty =
DependencyProperty.Register("IsInStatusNone", typeof(bool), typeof(StatusResultCheckBox), new PropertyMetadata(false, IsRunningPropertyChanged));
private static void IsRunningPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var statusResultCheckBox = d as StatusResultCheckBox;
if (statusResultCheckBox != null)
{
if (statusResultCheckBox.IsRunning)
{
if (statusResultCheckBox.IsInStatusWarning)
{
statusResultCheckBox.StatusResultCheckMode = StatusResultCheckMode.Warning;
statusResultCheckBox.HasStatus = true;
}
else
{
statusResultCheckBox.StatusResultCheckMode = StatusResultCheckMode.None;
statusResultCheckBox.HasStatus = false;
}
}
}
}
public StatusResultCheckBox()
{
InitializeComponent();
}
public StatusResultCheckMode StatusResultCheckMode
{
get { return (StatusResultCheckMode)GetValue(StatusResultCheckModeProperty); }
set { SetValue(StatusResultCheckModeProperty, value); }
}
public bool IsRunning
{
get { return (bool)GetValue(IsRunningProperty); }
set { SetValue(IsRunningProperty, value); }
}
public bool HasStatus
{
get { return (bool)GetValue(HasStatusProperty); }
set { SetValue(HasStatusProperty, value); }
}
public bool IsInStatusWarning
{
get { return (bool)GetValue(IsInStatusWarningProperty); }
set { SetValue(IsInStatusWarningProperty, value); }
}
public bool IsInStatusNone
{
get { return (bool)GetValue(IsInStatusNoneProperty); }
set { SetValue(IsInStatusNoneProperty, value); }
}
}
public enum StatusResultCheckMode
{
None,
Warning
}
}
If you want to do something like this, it probably will be easier if you just apply a style to a existing checkbox.
You can get the default style from here and change what you need.
Ended up refactoring all this code and simplifying. Basically styled a togglebutton.
<ToggleButton Grid.Column="3" Style="{StaticResource WarningStyle}"/>
And in the resource file...
<Style x:Key="WarningStyle" TargetType="ToggleButton">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Visibility" Value="Collapsed" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Grid>
<Path Data="F1M874.094,289.369L854.3,254.63C854.028,254.151 853.515,253.856 852.958,253.856 852.403,253.856 851.89,254.151 851.617,254.63L831.824,289.369C831.555,289.84 831.559,290.416 831.835,290.883 832.111,291.348 832.618,291.634 833.165,291.634L872.752,291.634C873.299,291.634 873.805,291.348 874.081,290.883 874.357,290.416 874.361,289.84 874.094,289.369z"
Stretch="Uniform" Fill="#FFFCCE00" Width="20" Height="20" Margin="0,0,0,0" RenderTransformOrigin="0.5,0.5" StrokeThickness="0.5">
</Path>
<Path Data="M855.653,287.189L850.264,287.189 850.264,282.745 855.653,282.745 855.653,287.189z M855.653,279.41L850.264,279.41 850.264,266.077 855.653,266.077 855.653,279.41z"
Stretch="Uniform" Fill="Black" Width="3" Height="10" Margin="0.5,3,0,0" RenderTransformOrigin="0.5,0.5" StrokeThickness="0.5">
</Path>
<Popup Name="WarningMessagePopup"
Width="425"
Height="150"
HorizontalAlignment="Center"
HorizontalOffset="22"
PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}"
IsOpen="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsChecked}"
StaysOpen="False"
VerticalOffset="7">
<ListBox ItemsSource="{Binding Path=WarningList}">
<ListBox.Style>
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<Trigger Property="IsVisible" Value="True">
<Setter Property="Background" Value="#F8FCFF"/>
</Trigger>
</Style.Triggers>
</Style>
</ListBox.Style>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource WarningListFilterByOperationTypeConverter}" Mode="OneWay">
<Binding Path="Count" />
<Binding Path="Message" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Popup>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsInStatusWarning.Value}" Value="True" >
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
When using WindowChrome (downloadable here) to customize the nonclient area of a window, a natural starting point is to make a title bar that looks and acts identical to a standard title bar. This requires adding a "fake" application icon and title bar, because apparently WindowChrome disables those features (the minimize, maximize and close buttons still work.)
Here's what I have so far:
<Window x:Class="MyApp.MainWindow"
x:Name="MainWindowItself"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:shell="clr-namespace:Microsoft.Windows.Shell;assembly=Microsoft.Windows.Shell"
xmlns:local="clr-namespace:MyApp"
Title="My Application" Icon="App.ico" Height="350" Width="525">
<Window.Resources>
<Style TargetType="{x:Type local:MainWindow}">
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MainWindow}">
<Grid>
<Border Background="White" Margin="{Binding Source={x:Static shell:SystemParameters2.Current}, Path=WindowNonClientFrameThickness}">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Title}"
VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="32,8,0,0"/>
<Image x:Name="SystemMenuIcon" Source="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Icon}"
VerticalAlignment="Top" HorizontalAlignment="Left"
Margin="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(shell:WindowChrome.WindowChrome).ResizeBorderThickness}"
Width="{Binding Source={x:Static shell:SystemParameters2.Current}, Path=SmallIconSize.Width}"
shell:WindowChrome.IsHitTestVisibleInChrome="True" MouseDown="SystemMenuIcon_MouseDown">
</Image>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<TextBlock Text="Client area content goes here"/>
</Grid>
</Window>
Code behind:
private void SystemMenuIcon_MouseDown(object sender, MouseButtonEventArgs e)
{
var offs = SystemParameters2.Current.WindowNonClientFrameThickness;
SystemCommands.ShowSystemMenu(this, new Point(Left + offs.Left, Top + offs.Top));
}
This comes very close to working. The first problem is that after you click the application icon and the system menu appears, the menu should disappear if you click a second time--instead, the menu just redraws. Also, if you double-click then the window should close, but Image doesn't have a double-click event. How would you suggest adding these features?
To disable working of standard Chrome Buttons Just add an extra attribute CaptionHeight="0" in your XAML code of shell:WindowsChrome
So it will be like that
<Setter Property="shell:WindowChrome.WindowChrome">
<Setter.Value>
<shell:WindowChrome CaptionHeight="0" />
</Setter.Value>
</Setter>
To make a fake Chrome. Modify the Template to like this:
<ControlTemplate TargetType="Window">
<AdornerDecorator>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="35" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid x:Name="PART_Chrome" shell:WindowChrome.IsHitTestVisibleInChrome="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="105" />
</Grid.ColumnDefinitions>
<Image Source="Application Favicon Path" />
<TextBlock Grid.Column="1" Text="{TemplateBinding Title}" VerticalAlignment="Center" />
<StackPanel Orientation="Horizontal" Grid.Column="3" >
<Button Command="{Binding Source={x:Static shell:SystemCommands.MinimizeWindowCommand}}" >
<Path Data="M0,6 L8,6 Z" Width="8" Height="7" VerticalAlignment="Center" HorizontalAlignment="Center"
Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="2" />
</Button>
<Button x:Name="MaximizeButton" Command="{Binding Source={x:Static shell:SystemCommands.MaximizeWindowCommand}}" >
<Path Data="M0,1 L9,1 L9,8 L0,8 Z" Width="9" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center"
Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="2" />
</Button>
<Button x:Name="RestoreButton" Command="{Binding Source={x:Static shell:SystemCommands.RestoreWindowCommand}}" >
<Path Data="M2,0 L8,0 L8,6 M0,3 L6,3 M0,2 L6,2 L6,8 L0,8 Z" Width="8" Height="8" VerticalAlignment="Center" HorizontalAlignment="Center"
Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="1" />
</Button>
<Button Command="{Binding Source={x:Static shell:SystemCommands.CloseWindowCommand}}" >
<Path Data="M0,0 L8,7 M8,0 L0,7 Z" Width="8" Height="7" VerticalAlignment="Center" HorizontalAlignment="Center"
Stroke="{Binding Foreground, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Button}}" StrokeThickness="1.5" />
</Button>
</StackPanel>
</Grid>
<ContentPresenter Grid.Row="1" Content="{TemplateBinding Content}" />
</Grid>
</AdornerDecorator>
<ControlTemplate.Triggers>
<Trigger Property="WindowState" Value="Normal">
<Setter Property="Visibility" Value="Collapsed" TargetName="RestoreButton" />
<Setter Property="Visibility" Value="Visible" TargetName="MaximizeButton" />
</Trigger>
<Trigger Property="WindowState" Value="Maximized">
<Setter Property="Visibility" Value="Visible" TargetName="RestoreButton" />
<Setter Property="Visibility" Value="Collapsed" TargetName="MaximizeButton" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Also do command binding for proper working of Fake Chrome bar
public MainWindow()
{
this.CommandBindings.Add(new CommandBinding(SystemCommands.CloseWindowCommand, OnCloseWindow));
this.CommandBindings.Add(new CommandBinding(SystemCommands.MaximizeWindowCommand, OnMaximizeWindow, OnCanResizeWindow));
this.CommandBindings.Add(new CommandBinding(SystemCommands.MinimizeWindowCommand, OnMinimizeWindow, OnCanMinimizeWindow));
this.CommandBindings.Add(new CommandBinding(SystemCommands.RestoreWindowCommand, OnRestoreWindow, OnCanResizeWindow));
}
private void OnCanMinimizeWindow(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = this.ResizeMode != ResizeMode.NoResize;
}
private void OnCanResizeWindow(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = this.ResizeMode == ResizeMode.CanResize || this.ResizeMode == ResizeMode.CanResizeWithGrip;
}
private void OnCloseWindow(object sender, ExecutedRoutedEventArgs e)
{
Microsoft.Windows.Shell.SystemCommands.CloseWindow(this);
}
private void OnMaximizeWindow(object sender, ExecutedRoutedEventArgs e)
{
Microsoft.Windows.Shell.SystemCommands.MaximizeWindow(this);
}
private void OnMinimizeWindow(object sender, ExecutedRoutedEventArgs e)
{
Microsoft.Windows.Shell.SystemCommands.MinimizeWindow(this);
}
private void OnRestoreWindow(object sender, ExecutedRoutedEventArgs e)
{
Microsoft.Windows.Shell.SystemCommands.RestoreWindow(this);
}
My xaml is not exactly alike (I do not use WindowChrome but my own and I have a titlebar template), but I had the exact same problem and the solution should be usable for you as well.
First the easy one: for the doubleclick to work just use the ClickCount.
Then, geting the menu disappear requires keeping some state telling whether it is currently active or not: the trick is that different events are fired on the second click (as figured out by using Snoop. The first click is only a MousweDown, the second is MouseDown followed by MouseUp (my guess is that the up from the first click is handled by the sysmenu).
private bool inSysMenu = false;
void SystemMenuIcon_MouseDown( object sender, MouseButtonEventArgs e )
{
if( e.ClickCount == 1 && !inSysMenu )
{
inSysMenu = true;
ShowSystemMenu(); //replace with your code
}
else if( e.ClickCount == 2 && e.ChangedButton == MouseButton.Left )
{
window.Close();
}
}
void SystemMenuIcon_MouseLeave( object sender, MouseButtonEventArgs e )
{
inSysMenu = false;
}
void SystemMenuIcon_MouseUp( object sender, MouseButtonEventArgs e )
{
inSysMenu = false;
}