How to set button background based on item property - WPF - c#

I'm creating Radio Buttons dynamically that means I'm looping throught my database items and I'm displaying styled radio buttons and here's my code:
public ObservableCollection<Product> products = new ObservableCollection<Product>(ProductsController.SelectAllProducts());
if (products.Count > 0)
{
foreach(var item in products)
{
SolidColorBrush mySolidColorBrush = new SolidColorBrush();
mySolidColorBrush = (SolidColorBrush)(new BrushConverter().ConvertFrom("#004a80"));
RadioButton a = new RadioButton();
a.BorderThickness = new Thickness(1);
a.Background = Brushes.Green;
a.Foreground = new SolidColorBrush(Colors.Black);
a.BorderBrush = mySolidColorBrush;
a.Width = 118;
a.Height = 70;
a.Margin = new Thickness(5,0,0,5);
Style style = Application.Current.Resources["MyRadioButtonAsButtonStyle"] as Style;
a.Style = style;
a.ApplyTemplate();
a.Content = item.OrdinalNumber;
Image imgControl = (Image)a.Template.FindName("img", a);
Image imgControlWhite = (Image)a.Template.FindName("whiteImg", a);
TextBlock text = (TextBlock)a.Template.FindName("text", a);
if (fileNames.Count > 0)
{
if (!fileNames.Any(item.Description.Contains))
{
item.IsProcessed = true; // SETTING PROPERTY
}
}
a.Click += (object sender, RoutedEventArgs e) =>
{
var radioButton = sender as RadioButton;
MessageBox.Show(radioButton.Content.ToString());
};
text.Text = item.Title;
imgControl.Source = image;
spProducts.Children.Add(a);
}
}
I'm setting IsProcessed which I would like to use when I'm setting background of this radio button which is styled as button.
So for example if IsProcessed = true I would like to set Background to Green, otherwise I would like to set it to Red.
Here is my class:
enter code herepublic class Product : INotifyPropertyChanged
{
#region Attributes
private string _ordinalNumber;
private string _title;
private string _description;
private bool _isProcessed;
#endregion
#region Properties
public string OrdinalNumber
{
get { return _ordinalNumber; }
set { _ordinalNumber = value; NotifyPropertyChanged("OrdinalNumber"); }
}
public string Title
{
get { return _title; }
set { _title = value; NotifyPropertyChanged("Title"); }
}
public string Description
{
get { return _description; }
set { _description = value; NotifyPropertyChanged("Description"); }
}
public bool IsProcessed
{
get { return _isProcessed; }
set
{
_isProcessed = value; NotifyPropertyChanged("IsProcessed");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And here is my custom style where I've set background for first time (To Green):
<Style x:Key="MyRadioButtonAsButtonStyle" TargetType="RadioButton">
<Setter Property="FontSize" Value="15" />
<Setter Property="GroupName" Value="Option" />
<Setter Property="BorderThickness" Value="1.5" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<!-- and so on for each property...-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Border Height="{Binding ActualHeight, ElementName=parentStackPanel}" Width="{Binding ActualWidth, ElementName=parentStackPanel}" BorderBrush="#004a80" BorderThickness="{TemplateBinding BorderThickness}">
<Grid x:Name="gridProduct" Background="Green">
<Grid.RowDefinitions>
<RowDefinition Height="80*"></RowDefinition>
<RowDefinition Height="20*"></RowDefinition>
</Grid.RowDefinitions>
<Image x:Name="slika" Margin="10" Grid.Row="0" Width="Auto" Visibility="Visible"/>
<TextBlock x:Name="tekst" Foreground="White" Margin="0,0,0,3" Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="12"></TextBlock>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="gridProduct" Property="Background" Value="Orange"/>
<Setter TargetName="tekst" Property="Foreground" Value="White"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So basically I don't know how to check value of item.IsProcessed in app.xaml so I might somehow add setter for background depending on that prop value?
Any kind of help would be great!
Thanks!

How about adding a DataTriggerto your Style?:
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="gridProduct" Property="Background" Value="Orange"/>
<Setter TargetName="tekst" Property="Foreground" Value="White"/>
</Trigger>
<DataTrigger Binding="{Binding IsProcessed}" Value="True">
<Setter TargetName="gridProduct" Property="Background" Value="Green"/>
</DataTrigger>
</ControlTemplate.Triggers>
For the binding to work, you should set the DataContext of the RadioButton:
foreach (var item in products)
{
...
a.DataContext = item;
spProducts.Children.Add(a);
}
In general, you would use an ItemsControl that binds to the ObservableCollection<Product> and define the RadioButton in the ItemTemplate.

Related

One togglebutton controlling another togglebutton button

I have five toggle buttons
When the first toggle button is pressed, all remaining toggle buttons should be checked
The first toggle button xaml wrote:
<ToggleButton VerticalAlignment="Center" Width="230" Height="95" Command="{Binding Path=AllCommand}" CommandParameter="All" Style="{StaticResource ButtonStyle.AllButtonStyle}">
<TextBlock Text="full agreement" />
</ToggleButton>
The rest of the toglebutton xaml wrote:
<ToggleButton VerticalAlignment="Center" Width="66" Height="66" Command="{Binding Path=Agree1Command}" CommandParameter="Agree1" Style="{StaticResource ButtonStyle.check1ButtonStyle}" />
<Style x:Key="ButtonStyle.check1ButtonStyle" TargetType="ToggleButton">
<Setter Property="Background" Value="#d1d0cf" />
<Setter Property="Foreground" Value="#ffffff" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Button.Effect">
<Setter.Value>
<DropShadowEffect Color="Black" Direction="320" ShadowDepth="3" BlurRadius="5" Opacity="0.5" />
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsChecked" Value="False" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="#d1d0cf" />
</MultiTrigger>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Button.Background" Value="#332c27" />
<Setter Property="Button.Effect">
<Setter.Value>
<DropShadowEffect Color="Black" Direction="320" ShadowDepth="0" BlurRadius="0" Opacity="0" />
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
All togglebuton styles were applied equally as in the code
How can I check the other toggle button when I press the first toggle button?
be in desperate need of help
Slight modification to my above comment. Hopefully this works for you. You could try something like the following. Basically this creates a view model for both the list of toggle buttons and the toggle buttons themselves. The command for each toggle button is bound to the command on the datacontext for the ItemsControl, not the ItemsSource. The command then calls a service that invokes an event when the buttons are modified (eg; when they are all checked). The main viewmodel subscribes to this event so that it can pull the modified view models from the service.
View
<ItemsControl ItemsSource = "{binding Buttons}">
<ItemsControl.ItemTemplate>
<ToggleButton IsChecked = "{binding IsChecked}"
Text = "{binding ButtonText}"
IsChecked = "{binding Path=DataContext.ButtonClickedCommand}"/>
</ItemsControl.ItemTemplate>
</ItemsControl>
Button View Model
public class ButtonViewModel : ViewModelBase
{
private string buttonText;
public int ButtonText
{
get => buttonText;
set
{
myVar = value;
OnPropertyChanged(nameof(ButtonText));
}
}
private bool isChecked;
public bool IsChecked
{
get => isChecked;
set
{
isChecked = value;
OnPropertyChanged(nameof(IsChecked))
}
}
}
Main View Model
public class ListViewModel : ViewModelBase
{
public ObservableCollection<ButtonViewModel> Buttons { get; set; }
public ICommand ButtonClickedCommand;
private readonly ToggleButtonService buttonService;
public ListViewModel()
{
buttonService = new ToggleButtonClickedCommand(Buttons);
buttonService.ButtonChecked =+ UpdateButtons
ButtonClickedCommand = new ToggleButtonClickedCommand(buttonService);
}
private void UpdateButtons()
{
Buttons = buttonService.Buttons;
}
}
Toggle button Service
public class ToggleButtonService
{
private IEnumerable<ButtonViewModel> buttonViewModels;
public IEnumerable<ButtonViewModel> ButtonViewModels
{
get { return buttons; }
set
{
buttonViewModels = value;
ButtonsChecked?.Invoke();
}
}
public event Action ButtonChecked;
public ToggleButtonService(IEnumerable<ButtonViewModel> buttons)
{
buttonViewModels = buttons;
}
public void SelectOrUnSelectAllButtons()
{
List<ButtonViewModel> Buttons = new List<ButtonViewModel>(ButtonViewModels);
foreach(button in Buttons)
{
button.IsChecked = !button.IsChecked;
}
ButtonViewModels = Buttons;
}
}
ToggleButton Clicked Command
public class ToggleButtonClickedCommand : ICommand
{
private readonly ToggleButtonService ButtonService;
public ToggleBUttonClickedCommand(ToggleButtonService buttonService)
{
ButtonService = buttonService;
}
public event EventHandler? CanExecuteChanged;
public bool CanExecute(object? parameter)
{
return true;
}
public async void Execute(object? parameter)
{
ButonService.SelectOrUnSelectAllButtons();
}
}

Binding Style in Pages

I have Window1 and there are 2 pages Login and Register.
There is a CheckBox on the page and for it 2 styles from App.xaml are LightCheckBoxStyle and DarkCheckBoxStyle. And I would like the style to depend on the value of the bool LightStyle property.
Ie if LightStyle = true; then the CheckBox is LightCheckBoxStyle and vice versa if false then DarkCheckBoxStyle.
App.xaml
<Application x:Class="COP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ModernButton="clr-namespace:ModernButton;assembly=ModernButton"
xmlns:local="clr-namespace:COP"
xmlns:local1="clr-namespace:COP.ViewModel"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style x:Key="DefaultCheckBoxStyle" TargetType="local:UserCheckBox">
<Setter Property="BorderBrush" Value="#707070"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="Width" Value="30"/>
<Setter Property="Height" Value="30"/>
<Setter Property="Margin" Value="0,10,0,0"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="Background" Value="#C3C3C3"/>
<Style.Triggers>
<DataTrigger Binding="{Binding LightStyle}" Value="True">
<Setter Property="Background" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="LightCheckBoxStyle" TargetType="local:UserCheckBox" BasedOn="{StaticResource DefaultCheckBoxStyle}">
<Setter Property="Background" Value="White"></Setter>
</Style>
<Style x:Key="DarkCheckBoxStyle" TargetType="local:UserCheckBox" BasedOn="{StaticResource DefaultCheckBoxStyle}">
<Setter Property="Background" Value="#C3C3C3"></Setter>
</Style>
</Application.Resources>
Login.xaml
<Page x:Class="COP.Pages.Login"
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:COP.Pages"
xmlns:local1="clr-namespace:COP"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="400"
Title="Login">
<Page.Resources>
<local1:BoolToStyle x:Key="Convert"></local1:BoolToStyle>
</Page.Resources>
<Grid Height="Auto">
<local1:UserCheckBox Style="{StaticResource LightCheckBoxStyle}" x:Name="checkBox"></local1:UserCheckBox>
</Grid>
</Grid>
MainViewModel.cs
class MainViewModel : INotifyPropertyChanged
{
private Page Login;
private Page Register;
private bool _lightStyle = true;
public bool LightStyle
{
get { return _lightStyle; }
set { _lightStyle = value; OnPropertyChanged(); }
}
private Page _currentPage;
public Page CurrentPage
{
get { return _currentPage; }
set { _currentPage = value; OnPropertyChanged(); }
}
private double _frameOpacity;
public double FrameOpacity
{
get { return _frameOpacity; }
set { _frameOpacity = value; OnPropertyChanged(); }
}
public MainViewModel()
{
Login = new Pages.Login();
Register = new Pages.Register();
FrameOpacity = 1;
CurrentPage = Login;
LightStyle = true;
}
public async void SlowOpacity(string page)
{
await Task.Factory.StartNew(() =>
{
for (double i = 1.0; i > 0.0; i -= 0.1)
{
FrameOpacity = i;
Thread.Sleep(50);
}
if (page == "Login")
CurrentPage = Login;
else
CurrentPage = Register;
for (double i = 0.0; i < 1.1; i += 0.1)
{
FrameOpacity = i;
Thread.Sleep(50);
}
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
If you need, I can still lay out the code. BoolToStyle is empty because I do not know how to implement it.
UserCheckBox.xaml.cs
public partial class UserCheckBox : UserControl, INotifyPropertyChanged
{
public UserCheckBox()
{
InitializeComponent();
MouseLeftButtonUp += delegate (object sender, MouseButtonEventArgs e) { IsChecked = !IsChecked; };
}
#region
private bool _IsChecked = false;
#endregion
#region
public bool IsChecked
{
get { return _IsChecked; }
private set { _IsChecked = value; OnPropertyChanged("IsChecked"); }
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
Login.xaml.cs
public partial class Login : Page
{
public Login()
{
InitializeComponent();
DataContext = Application.Current.MainWindow.DataContext;
}
}
Triggers are there in WPF to have conditional setters in style.
For your scenario, you can set default Background property to #C3C3C3 and change it to White when LightStyle is True.
Use below style -
<Style x:Key="DefaultCheckBoxStyle" TargetType="local:UserCheckBox">
<Setter Property="BorderBrush" Value="#707070"/>
<Setter Property="FontSize" Value="15"/>
<Setter Property="Width" Value="30"/>
<Setter Property="Height" Value="30"/>
<Setter Property="Margin" Value="0,10,0,0"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Foreground" Value="Gray"/>
<Setter Property="Background" Value="#C3C3C3"/>
<Style.Triggers>
<DataTrigger Binding="{Binding LightStyle}" Value="True">
<Setter Property="Background" Value="White"/>
</DataTrigger>
</Style.Triggers>
</Style>

Directly bind data to a setter value

How could I bind a brush from my ViewModel to a setter value?
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{Binding SelectionBrush, Mode=OneWay}" />
<Setter Property="BorderBrush" Value="{Binding SelectionBrush, Mode=OneWay}" />
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
class Leerformular
{
public Leerformular()
{
SelectionBrush = (SolidColorBrush)(new BrushConverter()).ConvertFromString(settings.SelectionBrush);
}
public SolidColorBrush SelectionBrush
{
get { return selectionBrush; }
set
{
if (selectionBrush != value && value != null)
{
selectionBrush = value;
OnPropertyChanged("SelectionBrush");
}
}
}
}
When I execute the program nothing is happening when I select the cells.

wpf validation rules are not kicking in

I have a Xaml page with style property for a text box. Whenever a user clicks the button with the textbox empty I need to display the validation error. Right now even when the textbox is empty and the button is pressed, a messagebox with empty text pops up. Even the Applicatin Exception is not working.. Please help...
Xaml:
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right"
Foreground="Orange"
FontSize="12pt">
!!!!
</TextBlock>
<Border BorderBrush="Green" BorderThickness="1">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
My ViewModel Class:
public class Customer
{
private string _Text;
public string text
{
get
{
return _Text;
}
set
{
_Text = value;
if (string.IsNullOrEmpty(value))
{
throw new ApplicationException("Name is Mandatory");
}
}
}
public Customer()
{
}
private RelayCommand<object> _commandOkInstance;
public ICommand CommandOk
{
get
{
if (_commandOkInstance == null)
_commandOkInstance = new RelayCommand<object>(OkCommand);
return _commandOkInstance;
}
}
public void OkCommand(object obj)
{
MessageBox.Show(_Text);
}
}
Main Window.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Customer c = new Customer();
this.DataContext = c;
}
}

Weird behaviour on ListBox in WPF

I have created an applicatin that is able to register some users with an image. To list them, I show that images inside a ListBox, and then you can click on each user image to select it, as shown in this image.
This ListBox is using my own Template:
<Style TargetType="ListBox" x:Key="TiledListBox">
<Setter Property="Margin" Value="0,0,10,0"></Setter>
<Setter Property="Padding" Value="1"/>
<Setter Property="Background" Value="White" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Grid}}}" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Vertical" Width="80">
<Image Source="{Binding Path=Picture}" Width="80" Height="80"></Image>
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
The ListBox in XAML:
<ListBox Name="ListBoxUsers" ItemsSource="{Binding Path=ListObservableUsers, ElementName=SelectUser}" Style="{DynamicResource TiledListBox}"
ScrollViewer.HorizontalScrollBarVisibility="Hidden" MouseDoubleClick="ListBoxUsers_MouseDoubleClick" />
Code behind only fills the ListBox items using an ObservableCollection.
The problem comes here. When I try to click on a user that it's different to the first one, more than one user is selected, as shown in this other picture:
Any idea on why that can be happening?
I have to say that I'm using the same code in another Window and it works perfectly, but I can't see any difference between them...
EDIT: You can use this code behind to make the example work.
public partial class MainWindow
{
public ObservableCollection<DtoObject> ListObservableUsers { get; set; }
public MainWindow()
{
ListObservableUsers = new ObservableCollection<DtoObject>();
InitializeComponent();
Image image = new Image {Source = Extensions.LoadBitmap(new Bitmap(Properties.Resources.vaca))};
for (int i = 0; i < 4; i++)
{
DtoObject dtoObject = new DtoObject {Picture = image};
ListObservableUsers.Add(dtoObject);
}
}
}
public static class Extensions
{
public static BitmapSource LoadBitmap(Bitmap source)
{
IntPtr ip = source.GetHbitmap();
BitmapSource bs = null;
bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(ip,
IntPtr.Zero, Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
return bs;
}
}
public class DtoObject
{
public Image Picture { get; set; }
}
private void ListBoxUsers_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
AcceptUser();
}
private void AcceptUser()
{
if (ListBoxUsers.SelectedIndex < 0) return;
selectedUser = (DtoObject)ListBoxUsers.SelectedItem;
EventHandler handler = UserSelected;
if (handler != null)
handler(this, new EventArgs());
DialogResult = true;
Close();
}
The event goes in another Window, so I think the problem might not be here.

Categories

Resources