MVVMLight - ModernWindow - Validation - wpf

I have an issue with view validation using IDataErrorInfo in my model object.
I have an application with several pages using ModernWindow control.
At startup, the validation is working fine. But once I navigated one time on the view, when I come back to one of the view already visited the validation don't work any more but IDataErrorInfo valiation method is called, something miss me in the knowledge of the functioning of the framework.
If someone has already encountered this problem, he is welcome
Sample code for ViewModel :
public class MyViewModel : ViewModelBase
{
public readonly IDataAccessService ServiceProxy;
private User _myUser
public User MyUser
{
get { return _myUser; }
set
{
_myUser= value;
RaisePropertyChanged("MyUser");
}
}
public MyViewModel(IDataAccessService serviceProxy)
{
ServiceProxy = serviceProxy;
MyUser = new User();
ReadAllCommand = new RelayCommand(GetUsers);
SaveCommand = new RelayCommand<User>(SaveUser);
SearchCommand = new RelayCommand(SearchUser);
SendProctorCommand = new RelayCommand<User>(SendUser);
DeleteProctorCommand = new RelayCommand<User>(DeleteUser);
ReceiveUser();
}
private void ReceiveUser()
{
if (Proctor != null)
{
Messenger.Default.Register<MessageCommunicator>(this, (user) => {
this.MyUser= user.User;
});
}
}
private void SendUser(User user)
{
if (user!= null)
{
Messenger.Default.Send<MessageCommunicator>(new MessageCommunicator()
{
User = user
});
}
}
The entity code (only those parts which concern the problem) :
public partial class User : ObservableObject, IDataErrorInfo
{
[NotMapped]
public string Error
{
get
{
return this[null];
}
}
public string this[string columnName]
{
get
{
string result = null;
if (columnName == "Lastname")
{
if (string.IsNullOrEmpty(Lastname))
result = "Please enter a lastname";
else
if (Lastname.Length < 5)
result = "The lastname must have 5 characters at least";
}
...
return result;
}
}
A sample for one field in the XAML :
<TextBox Grid.Column="1" Grid.Row="0" x:Name="LastnameTextBox" TextWrapping="Wrap" Text="{Binding UpdateSourceTrigger=PropertyChanged, Path= MyUser.Lastname ,Mode=TwoWay, ValidatesOnDataErrors=True, NotifyOnValidationError=true}" LostFocus="LastnameTextBox_LostFocus" />
TextBoxStyle.Xaml :
<Style TargetType="TextBox" x:Key="StandardTextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel LastChildFill="true">
<Border Background="Red" DockPanel.Dock="right" Margin="5,0,0,0" Width="20" Height="20" CornerRadius="10"
ToolTip="{Binding ElementName=customAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">
<TextBlock Text="!" VerticalAlignment="center" HorizontalAlignment="center" FontWeight="Bold" Foreground="white">
</TextBlock>
</Border>
<AdornedElementPlaceholder Name="customAdorner" VerticalAlignment="Center" >
<Border BorderBrush="red" BorderThickness="1" />
</AdornedElementPlaceholder>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>

Sometimes the window AdornerLayer doesn't properly update when inner views change. I observed this for TabControl, where switching between tabs didn't always trigger the correct adorner updates. Other types of view-changing controls are probably affected by the same thing.
The solution is to specify adorner layers that are specific to the controls that will be rendered/hidden dynamically. A local AdornerLayer is created by wraping controls in an AdornerDecorator.
In case of TabControl, the transformation would be as follows:
<!-- Before -->
<TabControl>
<TabItem>
<Content/>
</TabItem>
</TabControl>
<!-- After -->
<TabControl>
<TabItem>
<AdornerDecorator>
<Content/>
</AdornerDecorator>
</TabItem>
</TabControl>
Your layout should have some similar container/content layout, where the AdornerDecorator can be included.

Related

How can I DataBind a textbox from the parent window with values from the child with MVVM?

I just took over a project from another programmer who is no longer here. It was created using the MVVM Pattern (using the MVVM Light toolkit). I am new to MVVM and have been trying to learn the basics fast. Currently I am having trouble getting a selected value from a Child Window back to the Parent Window.
From another post on SO I learned that I should use the same ViewModel for both the parent and the child so I think I have the basics right. However I have not been able to get the selected values back to the parent. Below is a sample set of code similar to the production code.
My ViewModel for both pages is here
public class MainViewModel : ViewModelBase
{
private Vendor selectedVendor = null;
List<Vendor> vendors;
public MainViewModel()
{
OpenVendorWindowCommand = new RelayCommand(VendorSelect);
VendorSelectedCommand = new RelayCommand(VendorSelected);
LoadVendors();
}
public ICommand OpenVendorWindowCommand { get; private set; }
public ICommand VendorSelectedCommand { get; private set; }
void VendorSelect()
{
Messenger.Default.Send(new NotificationMessage("SelectVendor"));
}
public Vendor SelectedVendor
{
get { return selectedVendor; }
set
{
if (selectedVendor != value)
{
selectedVendor = value;
RaisePropertyChanged();
}
}
}
void VendorSelected()
{
Console.WriteLine(SelectedVendor.VendorName);
}
public List<Vendor> Vendors
{
get
{
return vendors;
}
set
{
if (vendors != value)
{
vendors = value;
RaisePropertyChanged();
}
}
}
private void LoadVendors()
{
DataTable dt = new DataTable();
dt = Vendor.GetVendors();
Vendors = new List<Vendor>();
foreach (DataRow row in dt.Rows)
{
Vendors.Add(new Vendor()
{
VendorID = Convert.ToInt32(row["VendorID"]),
VendorCode = Convert.ToString(row["VendorCode"]),
VendorName = Convert.ToString(row["VendorName"])
});
}
}
}
I am at the point that the Child Window opens and I am able to select a vendor from a ListBox. After the selection I press a button (VendorSelectedCommand) and it is at that point I want the textbox on the Parent Window to be filled with the SelectedVendor.VendorName value.
This is the XAML from my Child Window
<StackPanel VerticalAlignment="Center">
<ListBox
Height="200"
Margin="5"
HorizontalAlignment="Stretch"
Background="GhostWhite"
ItemsSource="{Binding Vendors}"
SelectedItem="{Binding Path=SelectedVendor, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="3">
<StackPanel Margin="15">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="175" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
FontWeight="SemiBold"
Foreground="Black"
Text="{Binding VendorName}" />
<TextBlock
Grid.Column="1"
FontWeight="SemiBold"
Foreground="Black">
<Run Text=" (" />
<Run Text="{Binding VendorCode}" />
<Run Text=") " />
</TextBlock>
</Grid>
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button Command="{Binding VendorSelectedCommand}" Content="Send Vendor Back" />
</StackPanel>
And lastly this is the XAML for the Parent Window with what I think is the correct binding
<StackPanel VerticalAlignment="Center">
<TextBox Margin="10" Text="{Binding SelectedVendor.VendorName}" />
<Button
Margin="10"
Command="{Binding OpenVendorWindowCommand}"
Content="Select Vendor" />
</StackPanel>
I have tried every possible combination of Binding Syntax that I can think of and have tried multiple different ways in the code behind to catch and bind it but have not been able to get it right. What is missing from my ViewModel to make this work?
Edit For clarity (and in response to a comment) I am adding the DataContext, which I had in the Constructor of the Views.
public partial class VendorView : Window
{
private MainViewModel _vm = null;
public VendorView()
{
InitializeComponent();
_vm = new MainViewModel();
DataContext = _vm;
}
}
Edit #2 I am opening the second page with this. This is very simple sample app with only two pages so I didn't want to get bogged down with navigation until I have a better handle on Binding.
private void NotificationMessageReceived(NotificationMessage msg)
{
if (msg.Notification == "SelectVendor")
{
var vendorView = new VendorView();
vendorView.ShowDialog();
}
}

WPF, RichTextBox, SelectionBrush, not SelectionBackColor

In a WPF app RichTextBox, I'm trying to find a way to provide a background color for various words within the box. In the System.Windows.Forms version of the RichTextBox, there was a very simple way to do this:
richTextBox1.SelectionBackColor = color;
richTextBox1.AppendText(word);
However, the System.Windows.Controls version of RichTextBox only has SelectionBrush, and this same method does not work.
Is a background color for different words in the RichTextBox possible?
You can work with the FlowDocument within the RichTextBox
<RichTextBox>
<RichTextBox.Document>
<FlowDocument>
<Paragraph>
<Run Background="Red">Hello World</Run>
<LineBreak/>
<Run Background="Green">This is a colored</Run>
<Run>text.</Run>
</Paragraph>
</FlowDocument>
</RichTextBox.Document>
</RichTextBox>
Edit regarding your comments: a (nearly) full example showing two different approaches.
No matter what you end up doing to present your text in the UI, you should have some sort of logic that creates a suitable data model of your highlighted text. The following example uses a collection of TextFragment where each fragment is optionally marked as highlighted.
public class TextFragment
{
public TextFragment(string text, bool isHighlighted)
{
this.Text = text;
this.IsHighlighted = isHighlighted;
}
public string Text { get; private set; }
public bool IsHighlighted { get; private set; }
}
Also, for the sample I use a class TextEntry to manage original text, search text and the resulting text fragments. Note I inherit from a BaseViewModel class which implements some helper functions for INotifyPropertyChanged related things. The helper function bool SetProperty<T>(ref T store, T value, [CallerMemberName]string propertyName = null) will check whether value and store are equal, potentially update the store with value and raise a property changed notification. The return value indicates, whether the value was really different/changed.
public class TextEntry : BaseViewModel
{
public TextEntry()
{
TextParts = new ObservableCollection<TextFragment>();
}
private void UpdateTextParts()
{
TextParts.Clear();
if (string.IsNullOrEmpty(SearchText))
{
TextParts.Add(new TextFragment(OriginalText, false));
return;
}
int startAt = 0;
do
{
int next = OriginalText.IndexOf(SearchText, startAt, StringComparison.CurrentCultureIgnoreCase);
if (next == -1)
{
TextParts.Add(new TextFragment(OriginalText.Substring(startAt), false));
return;
}
else
{
if (next != startAt)
{
TextParts.Add(new TextFragment(OriginalText.Substring(startAt, next - startAt), false));
}
// add highlighted part
TextParts.Add(new TextFragment(OriginalText.Substring(next, SearchText.Length), true));
startAt = next + SearchText.Length;
}
} while (startAt < OriginalText.Length);
}
private string _OriginalText;
public string OriginalText
{
get { return _OriginalText; }
set
{
if (SetProperty(ref _OriginalText, value))
{
UpdateTextParts();
}
}
}
private string _SearchText;
public string SearchText
{
get { return _SearchText; }
set
{
if (SetProperty(ref _SearchText, value))
{
UpdateTextParts();
}
}
}
public ObservableCollection<TextFragment> TextParts { get; private set; }
}
You can create a multi-part text in the UI by appending multiple textblocks with different text settings in a horizontal StackPanel. This way, the text parts can be managed by an ItemsControl. Alternatively, you can use the RichTextBox with its Document property, but this needs some more handling in code behind.
Some initialization code in the main window and a method to update the document for the RichTextBox example:
public MainWindow()
{
InitializeComponent();
var vm = new TextEntry();
grid1.DataContext = vm;
// this trigger works, but don't ask about efficiency for a bigger application
vm.TextParts.CollectionChanged += TextParts_CollectionChanged;
}
void TextParts_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ObservableCollection<TextFragment> data = sender as ObservableCollection<TextFragment>;
var doc = richTextBox1.Document;
var paragraph = new Paragraph();
paragraph.Inlines.AddRange(data.Select(x =>
{
var run = new Run(x.Text);
if (x.IsHighlighted)
{
run.Background = Brushes.LightCoral;
}
return run;
}));
doc.Blocks.Clear();
doc.Blocks.Add(paragraph);
}
And the XAML content of the window:
<Grid x:Name="grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="Original Text: " Margin="3"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Searched Word: " Margin="3"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Highlighted Text: " Margin="3"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="Highlighted Text2: " Margin="3"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding OriginalText, UpdateSourceTrigger=PropertyChanged}" Margin="3"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding SearchText,UpdateSourceTrigger=PropertyChanged}" Margin="3"/>
<ItemsControl Grid.Row="2" Grid.Column="1" ItemsSource="{Binding TextParts}" Margin="3" IsTabStop="False">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Top"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Text}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding IsHighlighted}" Value="True">
<Setter Property="Background" Value="LightCoral"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<RichTextBox Grid.Row="3" Grid.Column="1" x:Name="richTextBox1" Margin="3" VerticalAlignment="Top" IsReadOnly="True"/>
</Grid>
Expected Program:
Two lines with text inputs. One for the original text, one for the searched text.
3rd line showing the original text with search highlights as TextBlocks.
4th line showing the original text with search highlights as RichTextBox.

MVVM LIght impossible to bind one view on a TabItem

I have spent more than 10 hours exploring most of what could find on the MVVM pattern and binding to a TabControl.
I prefer not giving my actual code, but my problem is slightly simple :
I'm developing an application to Import/Export IDE (Informatica Data Exchange) Articles
I've created a full DLL wich contains all Model Classes (it was a demand of the client to use that class in another app), that also contains an EF entity (in the form of a stored procedure, not tables)
I'v got an IndexMainViewModel and associated view in the App.Ressources with a defined DataTemplate that binds the V and the VM, which will contains the 2 tabs.
Each of those tabs has to display 2 different views : the ExportView which is related to my ExportViewModel, and the ImportView on the same buiding style.
For info : I've created a DLL that contains all my Models and WorkClasses (including my services and I also created a DAOlayout with interfaces there, all is great there)
I apologize if it does not look clear. If you need I'll put down my code.
Any simple Idea is most welcome.
Thanks.
In the design part i create two Tab, one is Upload and another is Download.
<UserControl.Resources>
<Style TargetType="TabItem" x:Key="MainTabItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TabItem">
<DockPanel Height="45" Width="245" >
<Separator Name="RightBorder" Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}"
Height="30" DockPanel.Dock="Right" />
<Grid Cursor="Hand" Width="245" Background="Transparent" >
<TextBlock Name="TabItemTitle" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"
</Grid>
<ContentPresenter ContentSource="Header"/>
</DockPanel>
<ControlTemplate.Triggers>
<Trigger Property="Name" Value="UploadTab">
<Setter TargetName="TabItemTitle" Property="Text" Value="Upload"/>
</Trigger>
<Trigger Property="Name" Value="DownloadTab">
<Setter TargetName="TabItemTitle" Property="Text" Value="Download"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Grid HorizontalAlignment="Center" Background="White">
<TabControl Name="MediaControl" SelectionChanged="TabControl_SelectionChanged" Padding="0">
<TabItem Name="UploadTab" Style="{StaticResource MainTabItem}">
<Border Name="UploadTabPanel">
</Border>
</TabItem>
<TabItem Name="DownloadTab" Style="{StaticResource MainTabItem}">
<Border x:Name="DownloadTabPanel">
</Border>
</TabItem>
</TabControl>
</Grid>
</UserControl>
After each Tab click SelectionChanged event will be fired and show your UI under the TabItem as child of a Border.In the SelectionChanged event, you have to add UI Part...
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
string tabItem = ((sender as TabControl).SelectedItem as TabItem).Name as string;
switch (tabItem)
{
case "UploadTab":
if (UploadInstance == null)
{
UploadInstance = new UploadInstance();
}
UploadTabPanel.Child = UploadInstance;
break;
case "DownloadTab":
if (DownloadInstance == null)
{
DownloadInstance = new DownloadInstance();
}
DownloadTabPanel.Child = DownloadInstance;
break;
}
}
catch (System.Exception ex) { }
}
I ended up to the answer myself. I was confused about the view binding in the xaml code. I figured it out simply with this :
View :
<TabControl ItemsSource="{Binding Views}">
<TabControl.ItemTemplate >
<!-- header template -->
<DataTemplate>
<TextBlock Text="{Binding Header}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<!--content template-->
<DataTemplate>
<views:ExportView/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
ViewModel :
public sealed class IndexMainViewModel : ViewModelBase
{
private ObservableCollection<TabItem> _views;
public ObservableCollection<TabItem> Views
{
get { return _views; }
set
{
_views = value;
RaisePropertyChanged(() => Views);
}
}
public IndexMainViewModel()
{
_views = new ObservableCollection<TabItem>();
_views.Add(new TabItem { Header = "Export", Content = new ExportViewModel()});
_views.Add(new TabItem { Header = "Import", Content = new ImportViewModel()});
}
}
I also created a TabItem class with a ViewModelBase object (MVVM Light class object). This observable collection of views can be displayed if you don't miss the namespace <views:ExportView/> in the xaml code.

WPF why my ObservableCollection object collection not update my ProgressBar

i have WPF application and i am using PcapDotNet DLLs to send packets through the Networkcard.
This is my object that represent Wireshark file:
public class WiresharkFile
{
public event PropertyChangedEventHandler PropertyChanged;
virtual public void NotifyPropertyChange(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
private string _file; // file path
private string _packets; // how many packet in file
private string _sentPackets; // how many packet sent
private string _progress; // percentage (_sentPackets/_packets) * 100
public int Progress
{
get { return _progress; }
set
{
_progress = value;
NotifyPropertyChange("Progress");
}
}
public void Transmit(WireshrkFile)
{
// here i am send the packets
}
}
My list that hold this objects:
public ObservableCollection<WireshrkFile> files{ get; set; }
As you can see in every file that in process i am calculate the Progress.
Now all my files is inside ListView and in this ListView i have Progress-Bar column.
This is my Progress-Bar style:
<!-- Progress-Bar style -->
<Style x:Key="CustomProgressBar" TargetType="ProgressBar" >
<Setter Property="Template" >
<Setter.Value>
<ControlTemplate TargetType="ProgressBar">
<Border BorderBrush="Transparent" BorderThickness="1" Background="LightGray" CornerRadius="0" Padding="0" >
<Grid x:Name="PART_Track">
<Rectangle x:Name="PART_Indicator" HorizontalAlignment="Left" Fill="#FF15669E" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Progress-Bar definition inside my ListView
<ListView.Resources>
<DataTemplate x:Key="MyDataTemplate">
<Grid Margin="-6">
<ProgressBar Name="prog" Maximum="100" Value="{Binding Progress}"
Width="{Binding Path=Width, ElementName=ProgressCell}"
Height="20" Margin="0" Background="#FFD3D0D0" Style="{StaticResource CustomProgressBar}" />
<TextBlock Text="{Binding Path=Value, ElementName=prog, StringFormat={}{0}%}" VerticalAlignment="Center"
HorizontalAlignment="Center" FontSize="11" Foreground="Black" />
</Grid>
</DataTemplate>
<ControlTemplate x:Key="ProgressBarTemplate">
<Label HorizontalAlignment="Center" VerticalAlignment="Center" />
</ControlTemplate>
</ListView.Resources>
So when the Wireshark file is in process i can see that it send the packets and the properties changing but the Propress-Bar is still with 0%.
So i made i little test and inside timer event click changed my WireshakFile Progress property and in this case i can see the my ListView Progress-Bar changing.
So my question is what i am doing wrong ?
Edit:
I also try something that looks very strange but this is woking:
Inside my Timer_Tick i try to loop over my ObservableCollection collection:
foreach (WiresharkFile item in files)
{
item.Progress = item.Progress;
}
As you can see it only make pointer to the same property but this is working.

How to get WPF DataBinding-to-an-object to work

In the following example I bind the XAML to a static object via ObjectDataProvider.
When the user changes information, I want it to automatically reflect in the XAML.
What I don't understand is:
how do I perpetuate the object? do I have to create a singleton? in the click event, how do I access the "object that is being edited"
eventually of course I want the data to be retrieved from a model which reads an XML file or web service, and I want of course my ViewModel to check my model every second or so to see if the data has changed and reflect this on the XAML.
How to I get THERE from HERE:
XAML:
<Window x:Class="TestBinding99382.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestBinding99382"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider
x:Key="DataSourceCustomer"
ObjectType="{x:Type local:Customer}" MethodName="GetCustomer"/>
<Style x:Key="DataRowStyle" TargetType="StackPanel">
<Setter Property="Orientation" Value="Horizontal"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Margin" Value="0 10 0 0"/>
<Setter Property="DataContext"
Value="{StaticResource DataSourceCustomer}"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</Window.Resources>
<DockPanel>
<StackPanel DockPanel.Dock="Top"
DataContext="{StaticResource DataSourceCustomer}"
Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding Path=LastName}"/>
<TextBlock Text=" ("/>
<TextBlock Text="{Binding Path=FullName}" FontWeight="Bold"/>
<TextBlock Text=")"/>
</StackPanel>
<StackPanel Style="{StaticResource DataRowStyle}">
<TextBlock Text="First Name:"/>
<TextBox Text="{Binding Path=FirstName}"
Width="200" Margin="3 0 0 0"/>
</StackPanel>
<StackPanel Style="{StaticResource DataRowStyle}">
<TextBlock Text="Last Name:"/>
<TextBox Text="{Binding Path=LastName}"
Width="200" Margin="3 0 0 0"/>
</StackPanel>
<StackPanel Style="{StaticResource DataRowStyle}">
<Button Content="Save Changes" Click="Button_Click"/>
</StackPanel>
</DockPanel>
</Window>
Code Behind:
using System.Windows;
using System.ComponentModel;
using System;
namespace TestBinding99382
{
public partial class Window1 : Window
{
private Customer _customer;
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//I want to edit the _customer object here
//and have it my changes automatically reflect in my XAML
//via the INotifyPropertyChanged inheritance.
}
}
public class Customer : INotifyPropertyChanged
{
private string _firstName;
private string _lastName;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
this.RaisePropertyChanged("FirstName");
this.RaisePropertyChanged("FullName");
}
}
public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
this.RaisePropertyChanged("LastName");
this.RaisePropertyChanged("FullName");
}
}
public string FullName
{
get
{
return String.Format("{0} {1}", _firstName, _lastName);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public static Customer GetCustomer()
{
return new Customer { FirstName = "Jim", LastName = "Smith" };
}
}
}
in the click event, how do I access
the "object that is being edited"
You can access a resource in the behind code using FindResource method, see below.
private void Button_Click(object sender, RoutedEventArgs e)
{
ObjectDataProvider objectDataProvider
= FindResource("DataSourceCustomer") as ObjectDataProvider;
_customer = objectDataProvider.Data as Customer;
}
For your other questions:
What is perpetuate? You do not have to create a singleton to databind in WPF if that is your question.
eventually of course I want the data to be retrieved from a model which reads an XML file or web service, and I want of course my ViewModel to check my model every second or so to see if the data has changed and reflect this on the XAML.
WPF databinding will automatically update your view of you use an INotifyPropertyChanged object. Unless for performance reasons you only want to update your view every second or so just stick to normal databinding.

Resources