WPF IDataErrorInfo issue - c#

I can't seem to find simple explanation of how to set it up can some one please help out?
I've read almost every tutorial and every single one don't explain completely, my problem is that I've already written some code but I am not sure what to write in the MainWindow.xamls.cs and how to get the validation to work.
Class
public class Person : IDataErrorInfo
{
public string Fname { get; set; }
public string Lname { get; set; }
public string Error
{
get { return ""; }
}
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Fname")
{
if (string.IsNullOrEmpty(Fname))
{
result = "First name is required.";
return result;
}
string st = #"!|#|#|\$|%|\?|\>|\<|\*";
if (Regex.IsMatch(Fname, st))
{
result = "Contains invalid characters.";
return result;
}
}
if (columnName == "Lname")
{
if (string.IsNullOrEmpty(Lname))
{
result = "Cannot be empty.";
return result;
}
}
return null;
}
}
}
Xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication2"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ControlTemplate x:Key="eTemplate">
<DockPanel LastChildFill="True">
<TextBlock DockPanel.Dock="Right" Foreground="Blue" FontSize="13" Text="{Binding ElementName=adorned,Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" >
</TextBlock>
<Border BorderBrush="Red" BorderThickness="2">
<AdornedElementPlaceholder x:Name="adorned"/>
</Border>
</DockPanel>
</ControlTemplate>
</Window.Resources>
<Grid>
<TextBox Height="23" Validation.ErrorTemplate="{StaticResource ResourceKey=eTemplate}" HorizontalAlignment="Left" Margin="198,71,0,0" Name="Fname" VerticalAlignment="Top" Width="120" FontSize="15">
<TextBox.Text>
<Binding Path="Fname" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<TextBox Height="23" Validation.ErrorTemplate="{StaticResource ResourceKey=eTemplate}" HorizontalAlignment="Left" Margin="198,130,0,0" Name="Lname" VerticalAlignment="Top" Width="120" FontSize="15">
<TextBox.Text>
<Binding Path="Lname" ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged"/>
</TextBox.Text>
</TextBox>
<Label Content="FirstName" FontSize="14" Height="28" HorizontalAlignment="Left" Margin="114,71,0,0" Name="FirstName" VerticalAlignment="Top" FontFamily="Consolas" RenderTransformOrigin="0.063,0.607" Width="84"/>
<Label Content="LastName" FontSize="14" Height="28" HorizontalAlignment="Left" Margin="114,130,0,0" Name="LastName" VerticalAlignment="Top" FontFamily="Consolas" Width="79"/>
<Button x:Name="Add" Content="test" HorizontalAlignment="Left" Margin="198,186,0,0" VerticalAlignment="Top" Width="120"/>
</Grid>
</Window>
What do I do next?

Actually you've not implemented INotifyPropertyChanged interface so you property change notification is not performed. I've done some changes in your Person class as below;
public class Person : IDataErrorInfo, INotifyPropertyChanged
{
private string _fname;
private string _lname;
public String Fname
{
get { return _fname; }
set { _fname = value; OnPropertyChanged("Fname"); }
}
public String Lname
{
get { return _lname; }
set { _lname = value; OnPropertyChanged("Lname"); }
}
public string Error
{
get { return ""; }
}
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Fname")
{
if (string.IsNullOrEmpty(Fname))
{
result = "First name is required.";
return result;
}
string st = #"!|#|#|\$|%|\?|\>|\<|\*";
if (Regex.IsMatch(Fname, st))
{
result = "Contains invalid characters.";
return result;
}
}
if (columnName == "Lname")
{
if (string.IsNullOrEmpty(Lname))
{
result = "Cannot be empty.";
return result;
}
}
return null;
}
}
#region INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(String param)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(param));
}
}
#endregion
}
And in MainWindow.cs class, just set the DataContext as Person class;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Person();
}
}

Related

Calculate the textbox value based on the value of combobox ? ( wpf )

I have a small program, because I'm new, my program is to bring data from sql sever to textbox via combobox option and use the value shown in that textbox to calculate the +
I have made it to the step of putting up the data, now thanks to you to help me with the value calculation in the textbox, thank you for your help.
xaml code :
<Window x:Class="comboboxapp1.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:comboboxapp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:SimpleMath x:Key="MyFriends"/>
</Window.Resources>
<Grid>
<Label Content="code" HorizontalAlignment="Left" Margin="38,52,0,0"
VerticalAlignment="Top" Width="46" Height="23"/>
<Label Content="pieces" HorizontalAlignment="Left" Margin="38,126,0,0"
VerticalAlignment="Top" Width="46" Height="23"/>
<Label Content="layers" HorizontalAlignment="Left" Margin="38,196,0,0"
VerticalAlignment="Top" Width="46" Height="30"/>
<Label Content="production pieces" HorizontalAlignment="Left"
Margin="0,278,0,0" VerticalAlignment="Top" Width="108" Height="25"/>
<TextBox x:Name="txtcode"
Text="{Binding Txtcode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23" Margin="124,52,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<TextBox x:Name="txtpieces"
Text="{Binding Txtpieces, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23"
Margin="124,133,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<TextBox x:Name="txtlayers"
Text="{Binding Txtlayers,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23"
Margin="124,203,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<TextBox x:Name="txtproductionpieces"
Text="{Binding Txtproductionpieces,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23" Margin="124,280,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<ComboBox x:Name="comboBox1" ItemsSource="{Binding Source={StaticResource MyFriends}}"
HorizontalAlignment="Left" Margin="418,52,0,0" VerticalAlignment="Top"
Width="319" Height="36" SelectionChanged="ComboBox1_SelectionChanged"/>
<TextBox x:Name="txtseccond"
Text="{Binding Txtseccond,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="23"
Margin="124,345,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="141"/>
<Label Content="seccond" HorizontalAlignment="Left" Margin="38,345,0,0"
VerticalAlignment="Top" Width="46" Height="23"/>
<TextBlock Text="{Binding A, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Margin="418,133,0,0" TextWrapping="Wrap"
VerticalAlignment="Top" Height="23" Width="248"/>
<TextBox Text="{Binding No1,Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Left" Height="21" Margin="426,210,0,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="303"/>
</Grid>
</Window>
C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.SqlClient;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace comboboxapp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public SimpleMath Formular { get; set; }
public object SelectedValue { get; private set; }
public MainWindow()
{
Formular = new SimpleMath()
{
Txtcode = 0,
Txtpieces = 0,
Txtlayers = 0,
Txtproductionpieces = 0,
Txtseccond = 0,
};
InitializeComponent();
DataContext = Formular;
Fillcombobox();
}
private void MainWindow_Load(object sender, EventArgs e)
{
}
public void Fillcombobox()
{
SqlConnection con = new SqlConnection("Data Source=LEAN-22\\SQLEXPRESS;Initial
Catalog=LUAT;Integrated Security=True");
string sql = " select * from comboboxnew ";
SqlCommand cmd = new SqlCommand(sql, con);
SqlDataReader myreader;
try
{
con.Open();
myreader = cmd.ExecuteReader();
while (myreader.Read())
{
string sname = myreader.GetInt32(0).ToString();
comboBox1.Items.Add(sname);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public class SimpleMath : INotifyPropertyChanged
{
private int no1;
public int No1
{
get { return no1; }
set
{
no1 = value;
OnPropertyChanged("No1");
OnPropertyChanged("A");
}
}
private int txtcode;
public int Txtcode
{
get { return txtcode; }
set
{
txtcode = value;
OnPropertyChanged("Txtcode");
OnPropertyChanged("A");
}
}
private int txtpieces;
public int Txtpieces
{
get { return txtpieces; }
set
{
txtpieces = value;
OnPropertyChanged("Txtcode");
OnPropertyChanged("A");
}
}
private int txtlayers;
public int Txtlayers
{
get { return txtlayers; }
set
{
txtlayers = value;
OnPropertyChanged("Txtlayers");
OnPropertyChanged("A");
}
}
private int txtproductionpieces;
public int Txtproductionpieces
{
get { return txtproductionpieces; }
set
{
txtproductionpieces = value;
OnPropertyChanged("Txtproductionpieces");
OnPropertyChanged("A");
}
}
private int txtseccond;
public int Txtseccond
{
get { return txtseccond; }
set
{
txtseccond = value;
OnPropertyChanged("Txtseccond");
OnPropertyChanged("A");
}
}
public double A => No1;
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName()] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private void ComboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
SqlConnection con = new SqlConnection("Data Source=LEAN-22\\SQLEXPRESS;Initial
Catalog=LUAT;Integrated Security=True");
// string sql = " select * from comboboxnew where code = '" + comboBox1.Text+ "';";
string sql = " select * from comboboxnew where code = '" + comboBox1.SelectedItem +
"';";
//Console.WriteLine(comboBox1.Text);
//MessageBox.Show(comboBox1.Text);
SqlCommand cmd = new SqlCommand(sql, con);
SqlDataReader myreader;
try
{
con.Open();
myreader = cmd.ExecuteReader();
while (myreader.Read())
{
string code = myreader.GetInt32(0).ToString();
string pieces = myreader.GetInt32(1).ToString();
string layers = myreader.GetInt32(2).ToString();
string productionpieces = myreader.GetInt32(3).ToString();
string seccond = myreader.GetInt32(4).ToString();
txtcode.Text = code;
//txtcode.Text =SelectedValue;
txtpieces.Text = pieces;
//txtpieces.Text = "New value";
txtlayers.Text = layers;
//txtlayers.Text = "New value";
txtproductionpieces.Text = productionpieces;
//txtproductionpieces.Text = "New value";
txtseccond.Text = seccond;
//txtseccond.Text = "New value";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
From what I understand, you simply want to sum those five values into the TextBox, right?
If so, you can do that multiple ways...
One:
Have a property in the SimpleMath class that is the sum of the other values, and bind that to the TextBox. You would have to update the sum property whenever any of those five other ones updates.
Two:
You could use the IMultiValueConverter to sum the values without having any more properties in the SimpleMath class. It's done like this:
You create a class that implements the IMultiValueConverter inteface
public class SumConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.Cast<int>()?.Sum()?.ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
And use it in XAML like this
<TextBox>
<TextBox.Resources>
<local:SumConverter x:Key="SumConverter"/>
</TextBox.Resources>
<TextBox.Text>
<MultiBinding Converter="{StaticResource SumConverter}">
<Binding Path="Text" ElementName="OtherTextBox1" />
<Binding Path="Text" ElementName="OtherTextBox2" />
<Binding Path="Text" ElementName="OtherTextBox3" />
<Binding Path="Text" ElementName="OtherTextBox4" />
<Binding Path="Text" ElementName="OtherTextBox5" />
</MultiBinding>
</TextBox.Text>
</Control>
Hope this helps
EDIT
I've made a test project to show you the full code for both of my suggested solutions, here it is:
Solution one
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace StackOverflowTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new SimpleMath();
}
}
public class SimpleMath : INotifyPropertyChanged
{
private int _numberOne;
public int NumberOne
{
get => _numberOne;
set
{
_numberOne = value;
OnPropertyChanged();
CalculateSum();
}
}
private int _numberTwo;
public int NumberTwo
{
get => _numberTwo;
set
{
_numberTwo = value;
OnPropertyChanged();
CalculateSum();
}
}
private int _numberThree;
public int NumberThree
{
get => _numberThree;
set
{
_numberThree = value;
OnPropertyChanged();
CalculateSum();
}
}
private int _numberSum;
public int NumberSum
{
get => _numberSum;
set
{
_numberSum = value;
OnPropertyChanged();
}
}
private void CalculateSum()
{
NumberSum = NumberOne + NumberTwo + NumberThree;
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName()] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
<Window x:Class="StackOverflowTest.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:StackOverflowTest"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="200">
<StackPanel>
<TextBox x:Name="TxtOne" HorizontalAlignment="Stretch" Text="{Binding NumberOne}" Margin="10,10,10,0"/>
<TextBox x:Name="TxtTwo" HorizontalAlignment="Stretch" Text="{Binding NumberTwo}" Margin="10,10,10,0"/>
<TextBox x:Name="TxtThree" HorizontalAlignment="Stretch" Text="{Binding NumberThree}" Margin="10,10,10,0"/>
<TextBlock HorizontalAlignment="Center" Text="{Binding NumberSum}" Margin="10,10,10,0"/>
</StackPanel>
</Window>
Solution two
using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;
using System.Collections.Generic;
namespace StackOverflowTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new SimpleMath();
}
}
public class SimpleMath : INotifyPropertyChanged
{
private int _numberOne;
public int NumberOne
{
get => _numberOne;
set
{
_numberOne = value;
OnPropertyChanged();
}
}
private int _numberTwo;
public int NumberTwo
{
get => _numberTwo;
set
{
_numberTwo = value;
OnPropertyChanged();
}
}
private int _numberThree;
public int NumberThree
{
get => _numberThree;
set
{
_numberThree = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName()] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SumConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var numbers = new List<int>();
foreach (var item in values)
{
if(item is string stringValue)
{
try
{
numbers.Add(System.Convert.ToInt32(stringValue));
}
catch (Exception)
{
Console.WriteLine("Oops, not a number...");
}
}
}
return numbers.Sum().ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}
<Window x:Class="StackOverflowTest.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:StackOverflowTest"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="200">
<StackPanel>
<TextBox x:Name="TxtOne" HorizontalAlignment="Stretch" Text="{Binding NumberOne}" Margin="10,10,10,0"/>
<TextBox x:Name="TxtTwo" HorizontalAlignment="Stretch" Text="{Binding NumberTwo}" Margin="10,10,10,0"/>
<TextBox x:Name="TxtThree" HorizontalAlignment="Stretch" Text="{Binding NumberThree}" Margin="10,10,10,0"/>
<TextBlock HorizontalAlignment="Center" Margin="10,10,10,0">
<TextBlock.Resources>
<local:SumConverter x:Key="SumConverter"/>
</TextBlock.Resources>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource SumConverter}">
<Binding Path="Text" ElementName="TxtOne" />
<Binding Path="Text" ElementName="TxtTwo" />
<Binding Path="Text" ElementName="TxtThree" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
</Window>
Here's how it looks

Binding DataContext Window to ViewModel

Ok, I tried it several ways, but none worked as it should be in my case. I have a simple Window with a single ComboBox. I am changing the code to MVVM, so now everything is still in the Code-Behind and should go to a ViewModel, etc.
But even on the first step (binding the ViewModel to the View/Window) I don't seem to be able to bind them together.
My Window XAML:
<Window x:Class="CustomerGuidance.ClientWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:CustomerGuidance.ViewModels"
Title="Stop'n'Go - Client" Height="22" Width="229"
Loaded="ClientWindow_OnLoaded" WindowStyle="None"
WindowStartupLocation="Manual" Top="0" Left="0"
ResizeMode="NoResize" ShowInTaskbar="False" Topmost="True">
<Window.DataContext>
<VM:EmployeeViewModel />
</Window.DataContext>
<Canvas Background="Gainsboro">
<ComboBox Name="EmployeesComboBox"
ItemsSource="{Binding EmployeeEntries}"
Width="192" FontFamily="Arial" FontSize="14">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Lastname}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Surname}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Canvas>
The ViewModel looks like this:
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace CustomerGuidance.ViewModels
{
public class EmployeeViewModel : INotifyPropertyChanged
{
public EmployeeViewModel()
{
}
public static ObservableCollection<ServerWindow.EmployeeEntry> EmployeeEntries { get; set; } = new ObservableCollection<ServerWindow.EmployeeEntry>();
private string _surname;
private string _lastname;
private int _id;
public string Surname
{
get { return _surname; }
set
{
if (_surname == value)
return;
_surname = value;
NotifyPropertyChanged("Surname");
}
}
public string Lastname
{
get { return _lastname; }
set
{
if (_lastname == value)
return;
_lastname = value;
NotifyPropertyChanged("Lastname");
}
}
public int Id
{
get { return _id; }
set
{
if (_id == value)
return;
_id = value;
NotifyPropertyChanged("Id");
}
}
public virtual event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I get the following error message: "The Name "EmployeeViewModel" is not available in the namespace "clr-namespace:CustomerGuidance.ViewModels".And now the question: What am I missing? How can I bind the ViewModel to my window-XAML?
You should build your code for the errors to disappear.
It's because the namespace is not yet available in the assembly the designer relies on (your program) before it has been built.

How to changed button color at runtime using collection in wpf?

Student:
public class Student
{
private int _studentNo;
public int StudentNo
{
get { return _studentNo; }
set { _studentNo = value; }
}
private Rank _state;
public Rank State
{
get { return _state; }
set { _state = value; }
}
}
Rank:
public enum Rank
{
Pass,
Fail
}
RankBoard:
public class RankBoard:BindableBase
{
private static ObservableCollection<Student> _studentList;
public static ObservableCollection<Student> StudentList
{
get { return _studentList; }
set
{
_studentList = value;
}
}
static RankBoard()
{
LoadDetails();
}
private static void LoadDetails()
{
StudentList = new ObservableCollection<Student>()
{
new Student()
{
StudentNo=1,
State=Rank.Pass
},
new Student()
{
StudentNo=2,
State=Rank.Fail
},
new Student()
{
StudentNo=3,
State=Rank.Pass
},
};
}
public void ColorChanged(Student Items)
{
Student temp= _studentList.Where(i => i.StudentNo == Items.StudentNo).Single();
StudentList.Remove(temp);
StudentList.Add(Items);
}
}
BindableBase:
public class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string PropertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
BackgroundChange:
public class BackgroundChange : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var b = value as Button;
if(b!=null)
{
foreach (var x in RankBoard.StudentList.Where(i=>i.StudentNo==System.Convert.ToInt32(b.Content)))
{
if ((x.State.ToString()=="Pass"))
{
return new SolidColorBrush(Colors.Green);
}
else
{
return new SolidColorBrush(Colors.Red);
}
}
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Mainwindow.xaml
<Window x:Class="ButtonColorChanged.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:ButtonColorChanged"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:BackgroundChange x:Key="Color"/>
</Window.Resources>
<Grid>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0 30 0 0">
<TextBox x:Name="txt1" Width="75" Height="25"/>
<TextBox x:Name="txt2" Width="75" Height="25" Margin="20 0 20 0"/>
<Button x:Name="btnadd" Width="75" Height="25" Content="Add" Click="btnadd_Click"/>
</StackPanel>
<StackPanel VerticalAlignment="Center">
<Button x:Name="btn1" Content="1" Width="75" Height="25" Background="{Binding ElementName=btn1,Converter={StaticResource Color}}"/>
<Button x:Name="btn2" Content="2" Width="75" Height="25" Margin="0 20 0 20" Background="{Binding ElementName=btn2,Converter={StaticResource Color}}"/>
<Button x:Name="btn3" Content="3" Width="75" Height="25" Background="{Binding ElementName=btn3,Converter={StaticResource Color}}"/>
</StackPanel>
</Grid>
</Window>
I want to change button color at runtime if i change the collection of the data.
but if i put dynamicresource, exception will throw. so how do i do?
Binding:
<Button x:Name="btn1" Content="1" Width="75" Height="25" Background="{Binding Path=ButtonColor}"/>
Your class:
public class RankBoard : BindableBase
{
private Color _buttonColor;
public Color ButtonColor
{
get
{
return _buttonColor;
}
set
{
_buttonColor = value;
RaisePropertyChanged("ButtonColor");
}
}
public void RefreshButtonColor(int content)
{
foreach (var x in StudentList.Where(i=>i.StudentNo==content))
{
if ((x.State.ToString()=="Pass"))
{
ButtonColor = Colors.Green;
}
else
{
ButtonColor = Colors.Red;
}
}
}
}
Call RefreshButtonColor whenever you want to change the color of the buttons according to the student list.
When I look at your code, you want to change the color for all button according to their content. If you use properties you have to define properties for each button. And in RefreshButtonColor you have to choose which property you want to update according to the input. This is just an example of how to change button color.

DataTemplate is not generating ListItemBox

I want to generate ListItemBox using DataTemplate but items are not generating. Please guide me where is the mistake. I have following code in MainWindow.xaml.
<Window x:Class="Offline_Website_Downloader.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:bd="clr-namespace:Offline_Website_Downloader"
Title="Offline Website Downloader" Background="#f5f6f7" Height="500" Width="800"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<bd:BindingController x:Key="BindingControllerKey" />
<DataTemplate x:Key="DownloadedWebsitesListBox">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal" Width="Auto">
<TextBlock FontWeight="Bold" FontSize="18" Width="480">
<Hyperlink NavigateUri="http://google.com">
<Label Content="{Binding Path=WebsiteTitle}" />
</Hyperlink>
</TextBlock>
<TextBlock Width="132" TextAlignment="right">
<TextBlock Text="Remaining Time: "/>
<TextBlock Name="TimeRemaining" Text="js"/>
</TextBlock>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ProgressBar Name="progress1" Maximum="100" Minimum="0" Value="30" Background="#FFF" Width="612" Height="10" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" Width="450">Status: <TextBlock Text="{Binding Path=Status}"/></TextBlock>
<TextBlock Width="162" TextAlignment="right">
<TextBlock Text="Downloading Speed: "/>
<TextBlock Name="DownloadingSpeed" Text="{Binding Path=DownloadingSpeed}"/>
</TextBlock>
</StackPanel>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ListBox Width="Auto"
Name="WebsiteList"
Grid.Column="1"
Grid.Row="2"
Grid.RowSpan="2"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource DownloadedWebsitesListBox}"
Margin="0,0,0,0">
</ListBox>
</Grid>
</window>
and MainWindow.xaml.cs
BindingController bc = new BindingController();
public MainWindow()
{
InitializeComponent();
bc.DownloadingSpeed = "40kb/s";
bc.WebsiteTitle = "WebsiteTitle";
bc.Status = "Downloading";
DataContext = bc;
}
and BindingController.cs
public class BindingController
{
public BindingController()
{
}
private string _WebsiteTitle;
public string WebsiteTitle
{
set { _WebsiteTitle = value; }
get { return _WebsiteTitle; }
}
private string _Status;
public string Status
{
set { _Status = value ; }
get { return _Status ; }
}
private string _DownloadStartDate;
public string DownloadStartDate
{
set { _DownloadStartDate = value; }
get { return _DownloadStartDate ; }
}
private string _DownloadingSpeed = "0 kb/s";
public string DownloadingSpeed
{
set { _DownloadingSpeed = value; }
get { return _DownloadingSpeed; }
}
}
Your problem is that you're binding a ListBox to an object that contains several properties, but really only represents a single object/state. The ListBox expects to display a list of items (i.e. IList, IBindingList, IEnumerable, ObservableCollection).
Assuming you want to display more than one download at a time, which I'm assuming given that you're using a ListBox, I would refactor the download properties into a separate class. You will also need to implement INotifyPropertyChanged on your properties so that when the values are changed, they will be shown in the UI.
public class Download : INotifyPropertyChanged
{
private string _WebsiteTitle;
public string WebsiteTitle
{
get { return _WebsiteTitle; }
set
{
if (_WebsiteTitle == value)
return;
_WebsiteTitle = value;
this.OnPropertyChanged("WebsiteTitle");
}
}
private string _Status;
public string Status
{
get { return _Status; }
set
{
if (_Status == value)
return;
_Status = value;
this.OnPropertyChanged("Status");
}
}
private string _DownloadStartDate;
public string DownloadStartDate
{
get { return _DownloadStartDate; }
set
{
if (_DownloadStartDate == value)
return;
_DownloadStartDate = value;
this.OnPropertyChanged("DownloadStartDate");
}
}
private string _DownloadingSpeed = "0 kb/s";
public string DownloadingSpeed
{
get { return _DownloadingSpeed; }
set
{
if (_DownloadingSpeed == value)
return;
_DownloadingSpeed = value;
this.OnPropertyChanged("DownloadingSpeed");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if(this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
The new BindingController:
public class BindingController
{
public BindingController()
{
this.Downloads = new ObservableCollection<Download>();
}
public ObservableCollection<Download> Downloads { get; private set; }
}
Setting up the bindings in XAML:
<ListBox Width="Auto"
Name="WebsiteList"
Grid.Column="1"
Grid.Row="2"
Grid.RowSpan="2"
ItemsSource="{Binding Downloads}"
ItemTemplate="{StaticResource DownloadedWebsitesListBox}"
Margin="0,0,0,0">
</ListBox>
Initializing the collection in MainWindow
Download download = new Download();
download.DownloadingSpeed = "40kb/s";
download.WebsiteTitle = "WebsiteTitle";
download.Status = "Downloading";
bc.Downloads.Add(download);
this.DataContext = bc;

populating data to second combo box on first combo box selection change in wpf application

I am developing a wpf application. here i have to populate 2nd combo box based on the first combo box selection.
my xaml as follows:
<Grid Height="194" Width="486">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="82*" />
<ColumnDefinition Width="404*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="72*" />
<RowDefinition Height="122*" />
</Grid.RowDefinitions>
<Label Content="Category" Height="28" HorizontalAlignment="Left" Margin="13,36,0,0" Name="lblCategory" VerticalAlignment="Top" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="18,32,0,0" Name="txtScenario" VerticalAlignment="Top" Width="343" Text="{Binding Scenario_Desc}" Grid.Column="1" Grid.Row="1" />
<Button Content="Save" Command="{Binding SaveData}" Height="23" HorizontalAlignment="Left" Margin="194,71,0,0" Name="btnSave" VerticalAlignment="Top" Width="75" Grid.Row="1" Grid.Column="1" />
<Button Content="Reset" Command="{Binding ClearData}" Height="23" HorizontalAlignment="Left" Margin="286,71,0,0" Name="btnReset" VerticalAlignment="Top" Width="75" Grid.Row="1" Grid.Column="1" />
<Label Content="Sub Category" Height="28" HorizontalAlignment="Left" Margin="13,70,0,0" Name="lblSubCategory" VerticalAlignment="Top" Grid.RowSpan="2" Grid.ColumnSpan="2" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="18,36,0,0" Name="cboCategory" VerticalAlignment="Top" Width="343"
ItemsSource="{Binding Path=Category}"
DisplayMemberPath="Category_Desc"
SelectedValuePath="Category_Id"
SelectedValue="{Binding Path=Category_Id, Mode=TwoWay}"
SelectedIndex="0"
Text="{Binding Category_Desc}" Grid.Column="1">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding CategorySelected}"
CommandParameter="{Binding SelectedValue, ElementName=cboCategory}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
<Label Content="Scenario" Grid.ColumnSpan="2" Height="28" HorizontalAlignment="Left" Margin="12,32,0,0" Name="lblScenario" VerticalAlignment="Top" Grid.Row="1" />
<ComboBox Height="23" HorizontalAlignment="Left" VerticalAlignment="Top" Width="343" Grid.Column="1" Grid.RowSpan="2" ItemsSource="{Binding Path=SubCategory}" Margin="18,70,0,0" Name="cboSubCategory"
DisplayMemberPath="Sub_Category_Desc"
SelectedValue="{Binding Path=Sub_Category_Id}"
SelectedValuePath="Sub_Category_Id"
Text="{Binding Sub_Category_Desc}" />
</Grid>
When I save, I want to clear all data and show the form to allow fresh selection.
When I save, throws an error.
System.NullReferenceException was unhandled
Message=Object reference not set to an instance of an object.
StackTrace: RelayCommand`1.CanExecute(Object parameter) .....
at System.Windows.Interactivity.InvokeCommandAction.Invoke(Object parameter)
my view model code is as follows.
namespace MYOWN
{
public class ScenarioViewModel:BaseViewModel
{
private ScenarioModel scenarioModel;
public event EventHandler<ModelViewEventArgs> Reset = delegate { };
ObservableCollection<CategoryViewModel> category = new ObservableCollection<CategoryViewModel>();
ObservableCollection<SubCategoryViewModel> subCategory = new ObservableCollection<SubCategoryViewModel>();
public ScenarioViewModel()
{
scenarioModel = new ScenarioModel();
scenarioModel.isNew = true;
PopulateCategory();
}
public ScenarioViewModel( ScenarioModel scenario)
{
this.scenarioModel = scenario;
PopulateCategory();
}
private void PopulateCategory()
{
List<BaseModel> categoryModelList = DataManger.GetData((BaseModel)new CategoryModel());
foreach (CategoryModel cat in categoryModelList)
{
category.Add(new CategoryViewModel(cat));
}
}
private void PopulateSubCategory(int category_id)
{
//clear the exsisting list
subCategory.Clear();
SubCategoryModel model = new SubCategoryModel();
model.category_id = category_id;
//get the sub Category data for given category
List<BaseModel> subCategoryModelList = DataManger.GetData(model);
//populate the collection
foreach (SubCategoryModel cat in subCategoryModelList)
{
subCategory.Add(new SubCategoryViewModel(cat));
}
}
public ObservableCollection<SubCategoryViewModel> SubCategory
{
get { return subCategory; }
set { subCategory = value; }
}
public ObservableCollection<CategoryViewModel> Category
{
get { return category; }
set { category = value; }
}
public ScenarioModel ScenarioModel
{
get { return scenarioModel; }
set { scenarioModel = value; }
}
public Int32 Scenario_Id
{
get
{
return scenarioModel.scenario_id;
}
set
{
scenarioModel.scenario_id = value;
RaisePropertyChanged("Scenario_Id");
}
}
public string Scenario_Desc
{
get
{
return scenarioModel.scenario_desc;
}
set
{
scenarioModel.scenario_desc = value;
RaisePropertyChanged("Scenario_Desc");
}
}
public Int32 Sub_Category_Id
{
get
{
return scenarioModel.sub_category_id;
}
set
{
scenarioModel.sub_category_id = value;
RaisePropertyChanged("Sub_Category_Id");
}
}
string sub_category_desc;
public string Sub_Category_Desc
{
get
{
return sub_category_desc;
}
set
{
sub_category_desc = value;
RaisePropertyChanged("Sub_Category_Desc");
}
}
int category_id;
public int Category_Id
{
get
{
return category_id;
}
set
{
category_id = value;
RaisePropertyChanged("Category_Id");
}
}
string category_desc;
public string Category_Desc
{
get
{
return category_desc;
}
set
{
category_desc = value;
RaisePropertyChanged("Category_Desc");
}
}
#region Commands
protected void SelectSubCategoryDataExecute(int param=0)
{
PopulateSubCategory(param);
}
protected bool CanSelectSubCategoryDataExecute(int param=0)
{
return true;
}
public ICommand CategorySelected
{
get
{
return new RelayCommand<int>(SelectSubCategoryDataExecute, CanSelectSubCategoryDataExecute);
}
}
protected override void SaveMasterDataExecute()
{
DataManger.Save((BaseModel)scenarioModel);
//Clear once Save the data
OnReset();
}
protected override bool CanSaveMasterDataExecute()
{
return true;
}
protected void OnReset()
{
ScenarioViewModel viewModel = new ScenarioViewModel();
if (viewModel != null)
{
Reset(this, new ModelViewEventArgs(viewModel));
}
}
protected override void ResetDataExecute()
{
OnReset();
}
protected override bool CanResetDataExecute()
{
return true;
}
#endregion
}
}
I want to get the parameter value from combo box one and use that to populate to the second.
First time loading is file, when saving, the CategorySelected commnd expects a parameter, but it is assigned null. How to handle the null value in the RelayCommand....
It sound like you should use Master Details Pattern.
here are examples showing how correctly implement the pattern in wpf.
WPF Master Details MVVM Application
MSDN Article
ps:
dont forget to set IsSynchronizedWithCurrentItem="true"

Categories