C# Wpf setting toggle button from behind code using INotifyPropertyChanged - c#

I have a simple toggle button which works perfectly well . I can click on the toggle button and change the image that it shows . What i now want to do is the same thing from the code behind . Found a link that is similar
EDIT : This is what i want to do
I read up on the following thread that tells exactly what i need to do
WPF ToggleButton.IsChecked binding does not work
Programmatically my code does not seem to have any effect . if i click on the UI it works but i really wanna change state from within the program . The below program is just a prototype .
I cant figure out whats wrong in my XAML or code . Finnally decided to paste all of it as it a test program !
Xaml :
<Window x:Class="ToggleButtonImageChange.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ToggleButtonImageChange"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Image Source="secured.jpg"
x:Key="MyImage1" />
<Image Source="unsecured.jpg"
x:Key="MyImage2" />
<Style TargetType="{x:Type ToggleButton}"
x:Key="MyToggleButtonStyle">
<Setter Property="Content"
Value="{DynamicResource MyImage2}" />
<Style.Triggers>
<Trigger Property="IsChecked"
Value="True">
<Setter Property="Content"
Value="{DynamicResource MyImage2}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ToggleButton Style="{StaticResource MyToggleButtonStyle}" Name="tgbtn" Margin="0,29,0,139" IsChecked="{Binding Path=isAdmin, Mode=TwoWay}"/>
</Grid>
</Window>
Code behind :
namespace ToggleButtonImageChange
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window,INotifyPropertyChanged
{
bool _isAdmin;
public MainWindow()
{
InitializeComponent();
isAdmin = true;
OnPropertyChanged("isAdmin");
}
public bool isAdmin
{
get
{
return _isAdmin;
}
set
{
_isAdmin = value;
OnPropertyChanged("isAdmin");
}
}
private void OnPropertyChanged(string p)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(p));
}
public event PropertyChangedEventHandler PropertyChanged;
}
I went into the debugger and saw that even though i set isAdmin to true the button isChecked remains false and hence the incorrect image is displayed . I cant quite understand what wrong did do & how to get the isChecked changed through code .

Try to change the xaml file to this:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="350" Width="525"
x:Name="TestWindow">
<Window.Resources>
<Image Source="secured.png" x:Key="MyImage1" />
<Image Source="unsecured.png" x:Key="MyImage2" />
<Style TargetType="{x:Type ToggleButton}" x:Key="MyToggleButtonStyle">
<Setter Property="Content" Value="{DynamicResource MyImage2}" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="{DynamicResource MyImage1}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<ToggleButton x:Name="tgbtn"
Margin="0,29,0,139"
Style="{StaticResource MyToggleButtonStyle}"
IsChecked="{Binding Path=isAdmin, Mode=TwoWay, ElementName=TestWindow}"/>
</Grid>
</Window>
Notice the default Content value use MyImage2, but the trigger set it to MyImage1 - they just need to be different images.
Also notice the x:Name="TestWindow" that I've add to root window element - it is used later in binding:
{Binding Path=isAdmin, Mode=TwoWay, ElementName=TestWindow}
This is basically all what is required to change to make it work as you expect, I believe.
Also you can leave you constructor in code behind like this, but this is optional changes:
public MainWindow()
{
InitializeComponent();
isAdmin = true;
}
Hope that helps.

Related

Why grid background color covers the whole window if set in application resources?

I'm wondering why setting background color of a grid in application resources results in whole window covered by grid background, even if I don't have grid panel specified in XAML main window file.
MainWindow.xaml:
<Window x:Class="TicTacToe.DesktopApp.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"
mc:Ignorable="d"
Title="Tic-tac-toe"
Height="420"
Width="420"
ResizeMode="NoResize"
WindowStyle="SingleBorderWindow">
<DockPanel>
<Button Content="Button"></Button>
</DockPanel>
</Window>
App.xaml:
<Application x:Class="TicTacToe.DesktopApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="10" />
</Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="Red" />
<!--Uncomment the line below to see that button seems to be hidden under the grid.-->
<!--<Setter Property="Opacity" Value="0.5" />-->
</Style>
</Application.Resources>
</Application>
MainWindow.xaml.cs and App.xaml.cs contain only auto generated code. Nothing special.
Visual Studio preview shows window as expected:
Instead of it I'm getting:
Questions
Why it behaves like that? Is there somewhere hidden and always present grid that overlays whole window and gets included by my styling rules? And if so, why it does and why it is applied with the observable delay of a fragment of a second?
That is a grid used by the Visual tree design tools to select elements in the visual tree when debugging. You can verify this using an event setter,and clicking the grid, or by running the app, not in debug mode.
<Style TargetType="Grid">
<Setter Property="Background" Value="Red" />
<EventSetter Event="PreviewMouseDown" Handler="Grid_PreviewMouseDown"/>
<!--Uncomment the line below to see that button seems to be hidden under the grid.-->
<!--<Setter Property="Opacity" Value="0.5" />-->
</Style>
,
public partial class App : Application
{
private void Grid_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
MessageBox.Show(VisualTreeHelper.GetParent(sender as Grid).ToString());
}
}

WPF / XAML / MVVM - Set state of checkboxes based on conditions

I am a traditional MVC programmer that just started using MVVM and I do not know how I would program the below scenario the MVVM-way. I probably need multi binding, but can someone please help me and write that code for me? I've spend hours trying to achieve this, but I just don't know how to do it...
Btw, I know how to set the values from my settings file in XAML, but don't know how to write the other logic, EG:
IsEnabled="{Binding Source={x:Static p:Settings.Default}, Path=Pref_QuickProcess}"
This is my scenario:
I have a simple preferences screen with two checkboxes:
□ Quick process (value is set from Settings.Default.Pref_QuickProcess)
□ Upload to youtube (value is set from Settings.Default.Pref_UploadToYoutube)
The following conditions apply:
If "Quick process" is true, "Upload to youtube" should always be set to false and must be disabled.
If "Quick process" is false, "Upload to youtube" should be enabled.
These are the only options:
This is my XAML:
<Window x:Class="SchismRecorder.PreferencesWindow"
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"
mc:Ignorable="d"
Title="Preferences" Height="450" Width="800">
<Grid>
<GroupBox Header="Debug settings" HorizontalAlignment="Left" Height="326" Margin="21,20,0,0" VerticalAlignment="Top" Width="733">
<StackPanel>
<CheckBox Content="Quick process" HorizontalAlignment="Left" x:Name="chkQuickProcess" />
<CheckBox Content="Upload to Youtube" HorizontalAlignment="Left" x:Name="chkUploadToYoutube" />
</StackPanel>
</GroupBox>
</Grid>
This is my code behind:
public partial class PreferencesWindow : Window
{
public PreferencesWindow()
{
InitializeComponent();
chkQuickProcess.IsChecked = Settings.Default.Pref_QuickProcess;
chkUploadToYoutube.IsChecked = Settings.Default.Pref_UploadToYoutube;
ConfigureCheckboxes();
chkQuickProcess.Click -= ChkQuickProcess_Click;
chkQuickProcess.Click += ChkQuickProcess_Click;
}
private void ChkQuickProcess_Click(object sender, RoutedEventArgs e)
{
ConfigureCheckboxes();
}
void ConfigureCheckboxes()
{
if (chkQuickProcess.IsChecked.HasValue)
{
var isChecked = chkQuickProcess.IsChecked.Value;
if (isChecked)
{
chkUploadToYoutube.IsChecked = false;
chkUploadToYoutube.IsEnabled = false;
}
else
{
chkUploadToYoutube.IsEnabled = true;
}
}
}
protected override void OnClosing(CancelEventArgs e)
{
Settings.Default.Pref_QuickProcess = chkQuickProcess.IsChecked ?? false;
Settings.Default.Pref_UploadToYoutube = chkUploadToYoutube.IsChecked ?? false;
Settings.Default.Save();
base.OnClosing(e);
}
}
How do I get rid of my code behind, and get the same result in XAML with things like data triggers, converters, multi binding?
Edit: I think I do not necessarily need a viewmodel with setters to implement this logic, and do it with data triggers ? / multi binding ? instead. But maybe that is not possible?
You probably don't need a view model just to set a few properties in the Settings class that have a certain interdependence. The following XAML should do most or perhaps all of what you are describing.
When the first Checkbox is checked, the IsChecked and IsEnabled properties of the second Checkbox are set to false. However, the Settings.Default.Pref_UploadToYoutube property value is not changed. Not sure if this is strictly required.
By default, the second CheckBox's IsChecked property is bound to Pref_UploadToYoutube via a Style Setter. A DataTrigger on the Pref_QuickProcess property replaces the Binding and sets IsChecked and IsEnabled to false.
Also note the new Binding Path syntax for binding to static properties.
<CheckBox Content="Quick process"
IsChecked="{Binding Path=(p:Settings.Default).Pref_QuickProcess}"/>
<CheckBox Content="Upload to Youtube">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="IsChecked"
Value="{Binding Path=(p:Settings.Default).Pref_UploadToYoutube}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=(p:Settings.Default).Pref_QuickProcess}"
Value="True">
<Setter Property="IsChecked" Value="False"/>
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
You may also simplify the Settings properties binding paths by assigning the Settings.Default instance once to the DataContext of the StackPanel parent of the CheckBoxes:
<StackPanel DataContext="{Binding Path=(p:Settings.Default)}">
<CheckBox Content="Quick process" IsChecked="{Binding Pref_QuickProcess}"/>
<CheckBox Content="Upload to Youtube">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="IsChecked" Value="{Binding Pref_UploadToYoutube}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Pref_QuickProcess}" Value="True">
<Setter Property="IsChecked" Value="False"/>
<Setter Property="IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</StackPanel>

Wpf Xaml change canvas property "Path.Fill" [duplicate]

I'm attempting to use a data-trigger on a style to change a property.
In compliance with the "Minimal, Complete and Verifiable Example" requirements...
To reproduce, first create a WPF application in Visual Studio.
Within the App.xaml.cs :
using System.ComponentModel;
using System.Windows;
namespace Foo{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application, INotifyPropertyChanged {
private bool _clicked;
public bool Clicked {
get { return this._clicked; }
set {
this._clicked = value;
this.PropertyChanged?.Invoke(
this, new PropertyChangedEventArgs( "Clicked" ) );
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Within the MainWindow.xaml :
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:lib="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:Foo"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d" x:Class="Foo.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<lib:Boolean x:Key="True">True</lib:Boolean>
</Window.Resources>
<Grid>
<Button x:Name="button" Click="button_Click">
<Viewbox>
<TextBlock Text="Unclicked">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger
Binding="{Binding
Clicked,
Source={x:Static Application.Current}}"
Value="{StaticResource True}">
<Setter Property="Text" Value="Clicked" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Viewbox>
</Button>
</Grid>
</Window>
Within the MainWindow.xaml.cs -
using System.Windows;
namespace Foo{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow( ) {
InitializeComponent( );
}
private void button_Click( object sender, RoutedEventArgs e ) {
( Application.Current as App ).Clicked = !( Application.Current as App ).Clicked;
}
}
}
As a side note - I tried setting the value of the data trigger to just "True", and that also did not work ( the trigger did not catch, and text did not change based on setting the property to a new value ).
So why is the data-trigger not catching or working here? ( Either with the static resource or the literal value )? Even more relevant - why am I getting this error? The "After a 'DataTrigger' is in use (sealed), it cannot be modified" error? And what is the proper method of accomplishing what I am trying to do here? ( Preferably still using a data-trigger and not a converter, since I do need to switch between two values ).
The local value assigned to the TextBlock's Text property has higher precedence than the value provided by the Setter in the DataTrigger. See Dependency Property Value Precedence for details.
Set the initial Text value by another Setter:
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="Unclicked"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Clicked,
Source={x:Static Application.Current}}"
Value="{StaticResource True}">
<Setter Property="Text" Value="Clicked" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
The error message you see when you use the Boolean resource is just the XAML designer complaining. There is no error at runtime.

App.xaml style not applying to windows

I want all textboxes in my application to have a dark grey background when they are disabled or read only. I have added a style to my App.xaml which I believe should apply the style throughout my app.
App.xaml:
<Application x:Class="XXXX.Deployment.Utils.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:XXXX.Deployment.Utils" >
<Application.Resources>
<local:InvertBoolConverter x:Key="invertBoolConverter" />
<Style TargetType="TextBox" >
<Style.Triggers>
<Trigger Property="IsReadOnly" Value="True">
<Setter Property="Background" Value="DarkGray" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="DarkGray" />
</Trigger>
</Style.Triggers>
</Style>
</Application.Resources>
The style is applying in the design window but not when I actually run the application. I created a window with just a single textbox and set the IsEnabled property to False so I am not sure why this style would not be applied.
TestWindow.xaml:
<Window x:Class="XXXX.Deployment.Utils.TestWindow"
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:XXXX.Deployment.Utils"
mc:Ignorable="d"
Height="200" Width="800">
<StackPanel>
<TextBox Margin="2" IsEnabled="False" />
</StackPanel>
TestWindow.xaml.cs:
namespace XXXX.Deployment.Utils
{
public partial class TestWindow : Window
{
public TestWindow()
{
InitializeComponent();
}
}
}
I launch the window from within a console application and use
TestWindow t = new TestWindow();
t.ShowDialog();
EDIT
I think this issue may be because the App.xaml build action is not 'ApplicationDefinition'. We have all our UI in a common utility class library so we get the error 'Library project file cannot specify ApplicationDefinition element'. Is it possible for us to have a global style for controls even if our UI is in a class library?

Change style of default text of Combo Box in XAML

I'm trying to use XAML to make a combo box that is a drop-down list, that has default text already in the box in Italics, and when you click on the drop down to expand the list of options, all of the options will be listed in normal text rather than Italics. When a selection is made, I want the selected option to still be normal rather than Italic, even when it is in the same place as the default text. I'm new to XAML, and I am not sure how to do this, or if it is even possible?
My combo box is for now, as follows, where the default text to be shown is in the property 'Text'. Basically, I want 'Default Text' to be italic, but nothing else.
<ComboBox x:Name="ColumnComboBox" Grid.Column="1" Width="200" Margin="0,2" IsEditable="True" Text="Default Text" FontWeight="Normal" />
Any help is much appreciated.
You need to do a bit more of work to achieve this.
try this
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded">
<Grid>
<ComboBox Name="comboBox1" Margin="40,55,192,225" FontStyle="Italic">
<ComboBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding}" ></Label>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Label Name="lbl" Content="{Binding}" ></Label>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="lbl" Property="FontStyle" Value="Normal"> </Setter>
<Setter TargetName="lbl" Property="Background" Value="AliceBlue"></Setter>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="lbl" Property="FontStyle" Value="Italic"></Setter>
</Trigger>
<Trigger Property="IsSelected" Value="False">
<Setter TargetName="lbl" Property="FontStyle" Value="Normal"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</Grid>
</Window>
and here for testing the style from code behind
using System.Collections.ObjectModel;
using System.Windows;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<string> observableCollection = new ObservableCollection<string>();
public MainWindow()
{
for (int i = 0; i < 10; i++)
{
observableCollection.Add( "item:"+ i.ToString());
}
InitializeComponent();
comboBox1.ItemsSource = observableCollection;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
}
It is possible, but as you already noticed, default Text and the text shown when an item selected is the same text. That's why it will be tricky to set default text's style different from selected item's text style. The simplest implementation is to listen to ComboBox's selection changed event. When an item selected, change ComboBox's FontStyle to Normal, and when no item selected change it to Italic.
<ComboBox x:Name="ColumnComboBox" SelectionChanged="ColumnComboBox_SelectionChanged" IsEditable="True" Text="Default Text" FontWeight="Italic">
<ComboBoxItem Content="Item 1" FontStyle="Normal"/>
</ComboBox>
private void ColumnComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ColumnComboBox.FontStyle = ColumnComboBox.SelectedItem != null ? FontStyles.Normal : FontStyles.Italic;
}
Or maybe you actually want a watermark behavior for ComboBox. Check this blog post on decent implementation of watermarked ComboBox, downloadable source code available here.

Categories

Resources