Is there any way to update listbox items text after updating it in textboxes? I wanna do it only with bindings if it is possible. Listbox is reading from list, but list isn't updating so it's never gonna change, unless I add new item to list. Here is code
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DockPanel VerticalAlignment="Top" HorizontalAlignment="Left">
<Button Name="dodaj" Width="40" Margin="5" Click="dodaj_Click">Dodaj</Button>
<Button Name="usun" Width="40" Margin=" 5" Click="usun_Click">Usuń</Button>
</DockPanel>
<DockPanel HorizontalAlignment="Left" VerticalAlignment="Center" DataContext="{Binding ElementName=lista, Path=osoba, Mode=TwoWay}">
<ListBox Name="lb" ItemsSource="{Binding}" Width="200" Height="230">
</ListBox>
</DockPanel>
<DockPanel HorizontalAlignment="Right" VerticalAlignment="top">
<WrapPanel>
<StackPanel>
<Label>Imię</Label>
<Label>Nazwisko</Label>
<Label>Email</Label>
<Label>Telefon</Label>
</StackPanel>
<StackPanel DataContext="{Binding ElementName=lb, Path=SelectedItem, UpdateSourceTrigger=LostFocus}" TextBoxBase.TextChanged="zmiana">
<TextBox Width="200" Margin="3" Name="imie" Text="{Binding Path=imie}"></TextBox>
<TextBox Width="200" Name="nazwisko" Text="{Binding Path=nazwisko}"></TextBox>
<TextBox Width="200" Margin="3" Name="email" Text="{Binding Path=email}"></TextBox>
<TextBox Width="200" Name="telefon" Text="{Binding Path=telefon}"></TextBox>
</StackPanel>
</WrapPanel>
</DockPanel>
</Grid>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
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.ComponentModel;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lista.Add(new dane("imie1", "nazwisko1", "email1", "tel1"));
lista.Add(new dane("imie2", "nazwisko2", "email2", "tel2"));
lb.DataContext = lista;
}
public class dane : INotifyPropertyChanged
{
public dane(string imie, string nazwisko, string email, string telefon)
{
this._imie = imie;
this.nazwisko = nazwisko;
this.osoba = nazwisko + " " + imie;
this.email = email;
this.telefon = telefon;
}
private string _imie;
public string imie
{
set
{
_imie = value;
OnPropertyChanged("Imie");
}
get
{
return _imie;
}
}
public string nazwisko { set; get; }
public string osoba { set; get; }
public string email { set; get; }
public string telefon { set; get; }
public override string ToString()
{
return osoba;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string value)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(value));
}
}
}
public ObservableCollection<dane> lista = new ObservableCollection<dane>();
//public List<dane> lista = new List<dane>();
/*public ObservableCollection<dane> lista
{
get { return (ObservableCollection<dane>)GetValue(listaProperty); }
set { SetValue(listaProperty, value); }
}
public static readonly DependencyProperty listaProperty =
DependencyProperty.Register("lista", typeof(ObservableCollection<dane>), typeof(Window), new UIPropertyMetadata(null));
*/
private void dodaj_Click(object sender, RoutedEventArgs e)
{
lista.Add(new dane("...", "", "", ""));
MessageBox.Show(lista[0].ToString());
}
private void usun_Click(object sender, RoutedEventArgs e)
{
lista.RemoveAt(lb.SelectedIndex);
}
}
}
Listbox shows instances of your dane class by their ToString() representation. You should bind listbox to osoba property directly with DisplayMemberPath. And also value of osoba property is not udated then you change imia and nazwisko. You should recalculate it on its getter and also raise PropertChanged("osoba") then properties nazwisko or imia are being changed.
Related
I used a ComboBox to load all the font on the computer and preview it.
Here is the XAML:
<Window x:Class="Sample.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:Sample"
mc:Ignorable="d"
Title="Demo" Height="450" Width="800" WindowStartupLocation="CenterScreen" WindowStyle="None" Loaded="Window_Loaded" Background="White">
<Window.Resources>
<local:FontFamilyConverter x:Key="FontFamilyConverter"></local:FontFamilyConverter>
</Window.Resources>
<Grid>
<ComboBox Margin="10,10,0,10" HorizontalContentAlignment="Stretch" Name="FontFaimlyCB" Height="50" Width="250" ItemsSource="{Binding Source={x:Static Fonts.SystemFontFamilies}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontFamily="{Binding Converter={StaticResource FontFamilyConverter}}" FontSize="20"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Window>
And here is code-behind:
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.Shapes;
using System.Threading;
using System.Globalization;
namespace Sample
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
public class FontFamilyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return new FontFamily(value.ToString());
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
When the first time I click the ComboBox, it always takes a long time to add the items(There are almost one thousand fonts on my computer and it will takes 3-4 seconds to finish it). But any other software such as word/photoshop and so on won't like this.
How can I make it add faster? Please help me, thank you.
You could try to use a VirtualizingStackPanel as the ItemsPanel and set the MaxDropDownHeight to a fairly small value in order not to render all containers immediately. And you shouldn't have to use a converter:
<ComboBox Margin="10,10,0,10" HorizontalContentAlignment="Stretch" Name="FontFaimlyCB" Height="50" Width="250"
ItemsSource="{Binding Source={x:Static Fonts.SystemFontFamilies}}"
MaxDropDownHeight="50">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontFamily="{Binding}" FontSize="20" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Create your own observable collection that allows you to suspend notifications when adding items. E.g.:
static void Main(string[] args)
{
var fonts = new ObservableCollectionEx<string>();
using (fonts.DeferCollectionChanged())
{
for (int i = 0; i < 100000; i++)
{
fonts.Add(Guid.NewGuid().ToString());
}
}
}
public class ObservableCollectionEx<T> : ObservableCollection<T>
{
private int deferLevel;
private bool collectionUpdateNeeded;
public ObservableCollectionEx()
{
}
public ObservableCollectionEx(IEnumerable<T> collection)
: base(collection)
{
}
public ObservableCollectionEx(List<T> collection)
: base(collection)
{
}
public override event NotifyCollectionChangedEventHandler CollectionChanged;
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (deferLevel == 0)
{
CollectionChanged?.Invoke(this, e);
collectionUpdateNeeded = false;
}
else
{
collectionUpdateNeeded = true;
}
}
public IDisposable DeferCollectionChanged()
{
++deferLevel;
return new DeferHelper(this);
}
private void EndDefer()
{
--deferLevel;
if (deferLevel == 0 && collectionUpdateNeeded)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
private class DeferHelper : IDisposable
{
private ObservableCollectionEx<T> collection;
public DeferHelper(ObservableCollectionEx<T> collection)
{
this.collection = collection;
}
public void Dispose()
{
if (collection != null)
{
collection.EndDefer();
collection = null;
}
}
}
}
The View : I have UserControl, which has a TextBox and a Label. When the "Enter" key is down, I want the Label to be updated with the value form the text box. For the sake of this example, I created a CarUserControl. I will be hosting a list of these in an ItemsControl in the MainWindow.
The Model : I have class Car, which will be the model.
The ViewModel : I do not have a ViewModel for the CarUserControl and the Car. I have one for the MainWindow instead - lets call this the MainViewModel.
I can get the commands propogated from the individual usercontrols to the MainViewModel, but I'm unsure about getting the values from the textboxes in the MainViewModel?
Here are some of the assumptions I'm making from what I`ve read online about MVVM (there are ofcourse some sources which say the assumptions are wrong).
1] Usercontrols should not have a ViewModel.
2] Usercontrols should only expose dependency properties, and not public properties with INotifyChanged or events.
So, the question is, how do I update the label, and access the TextBox value in the MainViewModel.
Here is the test code :
-----CarUserControl.xaml----
<UserControl x:Class="TestMVVM.CarUserControl"
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:TestMVVM"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="300" x:Name="thisUC">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0">--</Label>
<TextBox Grid.Column="1" Background="#FFE8D3D3" BorderThickness="0">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding KeyDownCommand, ElementName=thisUC}"
CommandParameter="{Binding}"/>
</TextBox.InputBindings>
</TextBox>
</Grid>
</UserControl>
-----CarUserControl.cs-----
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;
namespace TestMVVM
{
/// <summary>
/// Interaction logic for CarUserControl.xaml
/// The Usercontrol
/// </summary>
public partial class CarUserControl : UserControl
{
private static readonly DependencyProperty StrValueProperty = DependencyProperty.Register("StrValue", typeof(float), typeof(CarUserControl), new PropertyMetadata(null));
private static readonly DependencyProperty KeyDownCommandProperty = DependencyProperty.Register("KeyDownCommand", typeof(ICommand), typeof(CarUserControl), new PropertyMetadata(null)); //Enter key down in the text box
public CarUserControl()
{
InitializeComponent();
}
public string StrValue
{
get { return (string)GetValue(StrValueProperty); }
set { SetValue(StrValueProperty, value); }
}
/// <summary>
/// "Enter" key down
/// </summary>
public ICommand KeyDownCommand
{
get { return (ICommand)GetValue(KeyDownCommandProperty); }
set { SetValue(KeyDownCommandProperty, value); }
}
}
}
//---The Model--Car.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace TestMVVM
{
/// <summary>
/// A simple model
/// </summary>
class Car : INotifyPropertyChanged
{
public Car(string name) {
this.name = name;
}
private string name;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
-----Main View Model---
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace TestMVVM
{
/// <summary>
/// The Main View Model
/// </summary>
class MainViewModel : INotifyPropertyChanged
{
/// <summary>
/// The main view model
/// </summary>
public MainViewModel()
{
//Create some test data
cars = new ObservableCollection<Car>();
cars.Add(new Car("Audi"));
cars.Add(new Car("Toyota"));
cars.Add(new Car("Subaru"));
cars.Add(new Car("Volvo"));
}
public event PropertyChangedEventHandler PropertyChanged;
private ObservableCollection<Car> cars; //List of tensioner spools
private ICommand enterDownCommand;
public ObservableCollection<Car> Cars
{
get { return cars; }
set
{
cars = value;
OnPropertyChanged("Cars");
}
}
public ICommand EnterDownCommand
{
get
{
if (enterDownCommand == null)
{
enterDownCommand = new RelayMCommand<Car>(OnEnterDownCommand);
}
return enterDownCommand;
}
}
/// <summary>
/// Called when "Enter" key is down.
/// </summary>
/// <param name="obj"></param>
private void OnEnterDownCommand(Car obj)
{
//How do I get the text box value here?
Console.Write(">>"+obj.Name);
}
public void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
}
-----MainWindow---
<Window x:Class="TestMVVM.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:TestMVVM"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainViewModel x:Name ="MainVM"/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Viewbox>
<ItemsControl ItemsSource="{Binding Cars}" Margin="5" Width="200">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:CarUserControl Margin="5"
KeyDownCommand="{Binding Path=DataContext.EnterDownCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Viewbox>
</Grid>
</Grid>
</Window>
---Relay Command---
using System;
using System.Threading;
using System.Windows.Input;
namespace TestMVVM
{
/// <summary>
/// Same as the Relay Command, except this handles an array of generic type <T>
/// </summary>
/// <typeparam name="T">Generic type parameter</typeparam>
public class RelayMCommand<T> : ICommand
{
private Predicate<T> _canExecute;
private Action<T> _execute;
public RelayMCommand(Action<T> execute, Predicate<T> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
private void Execute(T parameter)
{
_execute(parameter);
}
private bool CanExecute(T parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public bool CanExecute(object parameter)
{
return parameter == null ? false : CanExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
var temp = Volatile.Read(ref CanExecuteChanged);
if (temp != null)
{
temp(this, new EventArgs());
}
}
}
}
A UserControl may inherit its DataContext from a parent window or the current item in an ItemsControl.
So if you bind your ItemsControl to an IEnumerable<Car>, each instance of the CarUserControl can bind directly to the Name property of the corresponding Car object:
<TextBox Text="{Binding Name}"
Grid.Column="1" Background="#FFE8D3D3" BorderThickness="0">
<TextBox.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding KeyDownCommand, ElementName=thisUC}"
CommandParameter="{Binding}"/>
</TextBox.InputBindings>
</TextBox>
This is because the UserControl automatically inherits the DataContext from its parent element which is the corresponding Car object in the ItemsControl in this case.
Trying to bind a listbox to an object. Code runs without errors but for some reason sample data doesn't appear in listbox
XAML: ucDataBindingObject.xaml
<UserControl x:Class="TheProject.UserControls.ucDataBindingObject"
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"
Name="DataBindingObject"
Width="Auto"
Height="Auto"
mc:Ignorable="d">
<Grid Width="130"
Height="240"
Margin="0">
<ListBox Name="lbObject"
Width="110"
Height="80"
Margin="10,7,-9.6,0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
DisplayMemberPath="Name"
ItemsSource="{Binding ElementName=ucDataBindingObject,
Path=Clients}" />
</Grid>
</UserControl>
C#: ucDataBindingObject.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
namespace TheProject.UserControls
{
public partial class ucDataBindingObject : UserControl
{
public List<Client> Clients { get; set; }
public ucDataBindingObject()
{
Clients = new List<Client>();
Clients.Add(new Client(1, "David")); // sample data
Clients.Add(new Client(2, "Helen"));
Clients.Add(new Client(3, "Joe"));
InitializeComponent();
}
}
C# Client.cs
using System;
using System.Linq;
namespace TheProject.UserControls
{
public class Client
{
public int ID { get; set; }
public string Name { get; set; }
public Client(int id, string name)
{
this.ID = id;
this.Name = name;
}
}
}
Update your ItemsSource Binding as
ItemsSource="{Binding Path=Clients}"
and in the constructor of your view, set its DataContext after InitializeComponents as
this.DataContext = this;
there is no element named ucDataBindingObject, its the class name of your usercontrol
OR change the elementname in binding to DataBindingObject, which you named your usercontrol
It seems like I read another question / answer on this site about this issue but I cannot recall what the answer was and now I cannot find the original post.
I am not a fan of the default error template in WPF. I understand how to change this error template. However, if I add some content to the end of, say, a textbox, the size of the textbox does not change and the added content will (potentially) get clipped. How do I alter the textbox (I believe the correct termonology is adorned element) in this scenario so that nothing gets clipped?
Here is the XAML for the error template:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder />
<TextBlock Foreground="Red" Text="Error..." />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is the XAML for a couple of textboxes in the form:
<StackPanel>
<TextBox Text="{Binding...}" />
<TextBox />
</StackPanel>
Here's a solution adapted from Josh Smith's article on Binding to (Validation.Errors)[0] without Creating Debug Spew.
The trick is to define a DataTemplate to render the ValidationError object and then use a ContentPresenterto display the error message. If there is no error, then the ContentPresenter will not be displayed.
Below, I have shared the code of the sample app that I created.
Here is the XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
SizeToContent="WidthAndHeight"
Title="MainWindow">
<StackPanel Margin="5">
<StackPanel.Resources>
<DataTemplate DataType="{x:Type ValidationError}">
<TextBlock Text="{Binding ErrorContent}" Foreground="White" Background="Red" VerticalAlignment="Center" FontWeight="Bold"/>
</DataTemplate>
</StackPanel.Resources>
<TextBox Name="TextBox1" Text="{Binding Text1, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<ContentPresenter Content="{Binding ElementName= TextBox1, Path=(Validation.Errors).CurrentItem}" HorizontalAlignment="Left"/>
<TextBox Name="TextBox2" Text="{Binding Text2, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
<ContentPresenter Content="{Binding ElementName= TextBox2, Path=(Validation.Errors).CurrentItem}" HorizontalAlignment="Left"/>
<Button Content="Validate" Click="Button_Click"/>
</StackPanel>
</Window>
The code behind file:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private ViewModel _ViewModel = null;
public MainWindow()
{
InitializeComponent();
_ViewModel = new ViewModel();
DataContext = _ViewModel;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
_ViewModel.Validate = true;
_ViewModel.OnPropertyChanged("Text1");
_ViewModel.OnPropertyChanged("Text2");
}
}
}
The ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace WpfApplication1
{
public class ViewModel : INotifyPropertyChanged, IDataErrorInfo
{
private string _Text1;
public string Text1
{
get { return _Text1; }
set
{
_Text1 = value;
OnPropertyChanged("Text1");
}
}
private string _Text2;
public string Text2
{
get { return _Text2; }
set
{
_Text2 = value;
OnPropertyChanged("Text2");
}
}
public bool Validate { get; set; }
#region INotifyPropertyChanged Implemenation
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region IDataErrorInfo Implementation
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
string errorMessage = string.Empty;
if (Validate)
{
switch (columnName)
{
case "Text1":
if (Text1 == null)
errorMessage = "Text1 is mandatory.";
else if (Text1.Trim() == string.Empty)
errorMessage = "Text1 is not valid.";
break;
case "Text2":
if (Text2 == null)
errorMessage = "Text2 is mandatory.";
else if (Text2.Trim() == string.Empty)
errorMessage = "Text2 is not valid.";
break;
}
}
return errorMessage;
}
}
#endregion
}
}
my xml is :
<Window.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</Window.Resources>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="381*" />
<ColumnDefinition Width="20*" />
<ColumnDefinition Width="101*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="110*" />
<RowDefinition Height="201*" />
</Grid.RowDefinitions>
<StackPanel Margin="320,0,0,0" Grid.RowSpan="2">
<ListView ItemsSource="{Binding employeeCollection}">
<ListView.View>
<GridView>
<GridViewColumn Header="Employee ID" DisplayMemberBinding="{Binding Path=EmployeeID}"/>
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding Path=FirstName}"/>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding Path=LastName}"/>
<GridViewColumn Header="start" DisplayMemberBinding="{Binding Path=startHR}"/>
<GridViewColumn Header="finish" DisplayMemberBinding="{Binding Path=finishHR}">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
<StackPanel Margin="2,0,0,137" Grid.RowSpan="2" Grid.ColumnSpan="2" Grid.Column="1">
<ListBox FontFamily="Guttman Yad-Brush" BorderBrush="AliceBlue" BorderThickness="5" ItemsSource="{Binding Path=dateItems}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectedValue="{Binding Path=dateItem}" Width="233" Height="164" />
</StackPanel>
<Button Click="Button_Click" Width="102" Height="34" Margin="0,98,-1,69" Grid.Row="1" Grid.Column="2" Content="בחר" FontFamily="Guttman Yad-Brush" Background="AliceBlue"></Button>
<TextBox Name="dateTextBox" Grid.Column="1" Margin="26,152,0,33" Grid.Row="1" FontFamily="Guttman Yad-Brush" Grid.ColumnSpan="2" />
<Calendar SelectedDate="{Binding Path=SelectedDate}" Height="168" Name="calendar1" Width="182" SelectedDatesChanged="calendar1_SelectedDatesChanged" Margin="66,68,485,115" Grid.RowSpan="2" />
</Grid>
-->
-->
this is the start window class :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.ComponentModel;
using System.Windows.Data;
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;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
ConnectionViewModel vm;
public MainWindow()
{
InitializeComponent();
vm = new ConnectionViewModel();
DataContext = vm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//((ConnectionViewModel)DataContext).dateItems = "test";
if (vm.cm.dateItem.ToString() != null)
{
dateTextBox.Text = vm.cm.dateItem.ToString();
vm.em.insert();
}
}
private void calendar1_SelectedDatesChanged(object sender, SelectionChangedEventArgs e)
{
string []s = calendar1.SelectedDate.ToString().Split(' ');
dateTextBox.Text = s[0];
}
}
public class ConnectionViewModel
{
public DateConectionModule cm;
public employeesGrid em;
public ConnectionViewModel()
{
cm = new DateConectionModule();
em = new employeesGrid();
}
public CollectionView dateItems
{
get { return cm.dateItems; }
}
public string dateItem
{
get {return cm.dateItem;}
set{ cm.dateItem = value;}
}
public CollectionView employeeCollection
{
get { return em.employeeCollection; }
}
}
public class DateConectionModule : INotifyPropertyChanged
{
public static string[] datesString = { "01.01.2011", "02.01.2011", "03.01.2011", "04.01.2011", "05.01.2011" };
public DateConectionModule()
{
employeesGrid em = new employeesGrid();
IList<dateItem> list = new List<dateItem>();
//query to database should be here
foreach (string dataString in datesString)
{
list.Add(new dateItem(dataString));
}
_dateItemList = new CollectionView(list);
}
private readonly CollectionView _dateItemList;
private string m_dateItem;
public CollectionView dateItems
{
get { return _dateItemList; }
}
public string dateItem
{
get { return m_dateItem; }
set
{
if (m_dateItem == value)
return;
m_dateItem = value;
OnPropertyChanged("dateItem");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class dateItem
{
public string Name { get; set; }
public dateItem(string name)
{
Name = name;
}
}
public class employeesGrid : INotifyPropertyChanged
{
private CollectionView _dateItemList;
private string m_dateItem;
public employeesGrid()
{
IList<employiesData> list = new List<employiesData>();
//query to database should be here
list.Add(new employiesData{
EmployeeID = "036854768",
FirstName = "yoav" ,
LastName = "stern",
startHR = "1600" ,
finishHR = "0200"});
_dateItemList = new CollectionView(list);
}
public void insert()
{
IList<employiesData> list = new List<employiesData>();
//query to database should be here
list.Add(new employiesData
{
EmployeeID = "0234235345",
FirstName = "shoki",
LastName = "zikri",
startHR = "1600",
finishHR = "0200"
});
_dateItemList = new CollectionView(list);
OnPropertyChanged("employeeCollection");
}
public CollectionView employeeCollection
{
get { return _dateItemList; }
set
{
if (_dateItemList == value)
return;
_dateItemList = value;
OnPropertyChanged("employeeCollection");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public class employiesData
{
public string EmployeeID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string startHR { get; set; }
public string finishHR { get; set; }
}
}
1.i want thar when insertTest is called the UI will load my new values
2.this is my first wpf work so any advices on how to make things more readable,effican,simple and notes about the my poor architecture i know it's crapy can some
Below my points
1- What is the use of ConnectionViewModel class , it is just warpping DataConnectionViewModel so i would suggest that you can get rid of ConnectionViewModel() and use DataConnectionViewModel.
2-i think you can Get rid of employeesGrid class because all you need a collection of employees so rather than useing a seprate collection class, make an observrablecollection in DataConnectionViewModel() class.
3- Use Wpf- Model -View-ViewModel Template this gives you better idea
4- i have just refacror your code and create a similar application which uses MVVM and ObservableCollection and much simpler to use.
my xaml
<Window.Resources>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</Window.Resources>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="381*" />
<ColumnDefinition Width="20*" />
<ColumnDefinition Width="101*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="110*" />
<RowDefinition Height="201*" />
</Grid.RowDefinitions>
<StackPanel Margin="320,0,0,0" Grid.RowSpan="2">
<ListView ItemsSource="{Binding EmpList}">
<ListView.View>
<GridView>
<GridViewColumn Header="Employee ID" DisplayMemberBinding="{Binding Path=EmployeeID}"/>
<GridViewColumn Header="First Name" DisplayMemberBinding="{Binding Path=FirstName}"/>
<GridViewColumn Header="Last Name" DisplayMemberBinding="{Binding Path=LastName}"/>
<GridViewColumn Header="start" DisplayMemberBinding="{Binding Path=startHR}"/>
<GridViewColumn Header="finish" DisplayMemberBinding="{Binding Path=finishHR}">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
<StackPanel Margin="2,0,0,137" Grid.RowSpan="2" Grid.ColumnSpan="2" Grid.Column="1">
<ListBox FontFamily="Guttman Yad-Brush" BorderBrush="AliceBlue" BorderThickness="5" ItemsSource="{Binding Path=dateItems}" DisplayMemberPath="Name" SelectedValuePath="Name" SelectedValue="{Binding Path=dateItem}" Width="233" Height="164" />
</StackPanel>
<!--<Button Click="Button_Click" Width="102" Height="34" Margin="0,98,-1,69" Grid.Row="1" Grid.Column="2" Content="בחר" FontFamily="Guttman Yad-Brush" Background="AliceBlue"></Button>-->
<TextBox Name="dateTextBox" Grid.Column="1" Margin="26,152,0,33" Grid.Row="1" FontFamily="Guttman Yad-Brush" Grid.ColumnSpan="2" />
<!--<Calendar SelectedDate="{Binding Path=SelectedDate}" Height="168" Name="calendar1" Width="182" SelectedDatesChanged="calendar1_SelectedDatesChanged" Margin="66,68,485,115" Grid.RowSpan="2" />-->
</Grid>
My Code
1-
Create DataConnectionViewModel which is inheriting ViewModelBase class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Employee.Models;
using System.Collections.ObjectModel;
namespace Employee.ViewModels
{
public class DateConectionModule : ViewModelBase
{
#region " Instance Variables "
public static string[] datesString = { "01.01.2011", "02.01.2011", "03.01.2011", "04.01.2011", "05.01.2011" };
#endregion " Instance Variables "
#region " Constructor "
public DateConectionModule()
{
CreateEmployeeData();
}
#endregion " Constructor "
#region " Public Properties "
public ObservableCollection<EmployeeData> EmpList { get; set; }
#endregion " Public Properties "
#region " Helper Methods "
private void CreateEmployeeData()
{
EmpList = new ObservableCollection<EmployeeData>();
EmpList.Add
(
new EmployeeData() { EmployeeID="1", LastName="Gates", FirstName="Bill", finishHR="", startHR ="" }
);
}
#endregion " Helper Methods "
}
}
2- ViewModelBAse Class
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
namespace Employee.ViewModels
{
///
/// Provides common functionality for ViewModel classes
///
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
3- Set the Data Context in the MainWindow.Xaml.cs
public partial class MainView : Window
{
public MainView()
{
InitializeComponent();
this.DataContext = new DateConectionModule();
}
}
there are lot of other things like Dependency Injection, etc.. but for your case , you can write a controller which call your dataservice to give you employess list than assign list to the observablecollection.
4- i can suggest read abot Prism framework which gives you very much flexibility during application management and also in TDD.