let's explain a little bit my issue, i wrote a code of an application that show a usercontrol with bindings that i'm trying to set to another sub-usercontrol. I'm using a model that i've set to my MainWindow.xaml.
THE PROBLEM IS:
The usercontrol innerusercontrol doesn't show changes in the bindings. I guess i made a mistake, but i don't know where and how to fix it. I read several posts but no body answer them with a specific code-line. Could any body help me?
I'll appreciate an answer. Thanks
MainWindow.xaml
<Window x:Class="DependencyProperties.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DependencyProperties"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="3*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<local:DisplayProduct x:Name="ucDisplay" Grid.Row="1" Height="110"/>
<Button Grid.Row="2" Click="Button_Click">Apply circle visibility false/true</Button>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public static ProductModel product;
public MainWindow()
{
InitializeComponent();
product = new ProductModel() { IsActivo = true, Descuento = 15.00, Importe = 100.00, Total = 85.00 };
ucDisplay.DataContext = product;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("Antes " + product.IsActivo);
product.IsActivo = !product.IsActivo;
Console.WriteLine("Despues" + product.IsActivo);
}
}
DisplayProductUserControl.xaml
<UserControl x:Class="DependencyProperties.DisplayProductUserControl"
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:DependencyProperties"
mc:Ignorable="d" x:Name="win_Display"
d:DesignHeight="80" d:DesignWidth="500">
<UserControl.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="18"/>
<Setter Property="FontWeight" Value="Light"/>
</Style>
</UserControl.Resources>
<Grid>
<Viewbox>
<Grid Width="{Binding ElementName=win_Display, Path=ActualWidth}" Height="{Binding ElementName=win_Display, Path=ActualHeight}">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Price: "/>
<TextBlock Text="$"/>
<TextBlock Text="{Binding Path=Price}"/>
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text="Discount: "/>
<TextBlock Text="$"/>
<TextBlock Text="{Binding Path=Discount}"/>
</StackPanel>
<StackPanel Grid.Column="2" Orientation="Horizontal">
<TextBlock Text="Total: "/>
<TextBlock Text="$"/>
<TextBlock Text="{Binding Path=Total}"/>
</StackPanel>
<local:InnerUserControl Grid.Column="0" Grid.Row="1">
<local:InnerUserControl DataContext="{Binding Path=IsActive,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=UserControl}}"/>
</local:InnerUserControl>
</Grid>
</Viewbox>
</Grid>
</UserControl>
InnerUserControl *THE PROBLEM IS HERE <------------------ *
<UserControl x:Class="DependencyProperties.InnerUserControl"
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"
mc:Ignorable="d" x:Name="win_inner"
d:DesignHeight="40" d:DesignWidth="100">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="VisibilityConverter"/>
</UserControl.Resources>
<Grid Width="{Binding ElementName=win_inner, Path=ActualWidth}">
<TextBlock Text="{Binding Path=IsActive}" FontSize="15"/>
**<!-- HERE IS THE PROBLEM I DOESN'T SHOW CHANGES WHEN IsActive is false/true -->**
<Ellipse Height="{Binding ElementName=win_inner, Path=ActualHeight}" Width="{Binding RelativeSource={RelativeSource Self}, Path=ActualHeight}" Fill="Red"
Visibility="{Binding Path=IsActive, Converter={StaticResource VisibilityConverter}}" HorizontalAlignment="Right"></Ellipse>
</Grid>
</UserControl>
ProductoModel
public class ProductModel : INotifyPropertyChanged
{
private double price;
public double Price
{
get { return price; }
set { price= value; }
}
private double discount;
public double Discount
{
get { return discount; }
set { discount= value; }
}
private double total;
public double Total
{
get { return total; }
set { total = value; }
}
private bool isActive;
public bool IsActive
{
get { return isActive; }
set { isActive = value; }
}
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
like dnr3 commented already,
Add RaisePropertyChanged method in your 'Product Model' class.
public bool IsActive
{
get { return isActive; }
set
{
isActive = value;
RaisePropertyChanged("IsActive");
}
}
View never changes until it gets 'PropertyChanged' Event.
Related
I want to print listbox item count alog with header of expander like
header1 (count) . How can I achieve this in WPF
You haven't provided any code for what you have done, such as if you are using a ViewModel or doing code-behind. There are multiple ways to go about it.
My ViewModel Way
In this example, I've made a ViewModel containing an ObservableCollection of string to populate the ListBox. The Expander header is bound to a property that is populated using a combination of the HeaderText and ItemCount.
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _headerText = string.Empty;
private string _headerTextFull = string.Empty;
private ObservableCollection<string> _listItems = new ObservableCollection<string>();
private int _itemCount = 0;
public ViewModel() { }
public string HeaderText
{
get { return _headerText; }
set
{
_headerText = value;
NotifyPropertyChanged("HeaderText");
UpdateHeader();
}
}
public string HeaderTextFull
{
get { return _headerTextFull; }
set
{
_headerTextFull = value;
NotifyPropertyChanged("HeaderTextFull");
}
}
public ObservableCollection<string> ListItems
{
get { return _listItems; }
set
{
_listItems = value;
NotifyPropertyChanged("ListItems");
ItemCount = (_listItems != null ? _listItems.Count : 0);
}
}
public int ItemCount
{
get { return _itemCount; }
set
{
_itemCount = value;
NotifyPropertyChanged("ItemCount");
UpdateHeader();
}
}
private void UpdateHeader()
{
HeaderTextFull = String.Format("{0} ({1})", _headerText, _itemCount);
}
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The XAML for the Window:
<Window x:Class="SO37192142.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Expander Grid.Row="0" Name="expander1" Header="{Binding Path=HeaderTextFull, FallbackValue='Items'}" IsExpanded="True">
<Grid>
<ListBox Name="listBox1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
</Grid>
</Expander>
<Button Grid.Row="1" Content="Add an Item" Click="Button_Click" />
</Grid>
</Window>
The code-behind:
public partial class Window1 : Window
{
ViewModel myModel = new ViewModel();
public Window1()
{
InitializeComponent();
myModel.ListItems.CollectionChanged += new NotifyCollectionChangedEventHandler(ListItems_CollectionChanged);
myModel.HeaderText = "Items";
myModel.ListItems.Add("Item 1");
myModel.ListItems.Add("Item 2");
this.DataContext = myModel;
}
void ListItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
myModel.ItemCount = myModel.ListItems.Count;
}
void Button_Click(object sender, RoutedEventArgs e)
{
myModel.ListItems.Add("Another item");
}
}
When it starts, the Expander header will say "Items (2)". Every time the button is clicked, the header will update to show the new count.
Slight Variation of Above
Here's an example that provides the above example, but also adds a second list to demonstrate a different way. Notice the Expander.Header section.
<Window x:Class="SO37192142.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Expander Grid.Row="0" Grid.Column="0" Name="expander1" Header="{Binding Path=HeaderTextFull, FallbackValue='Items'}" IsExpanded="True">
<Grid>
<ListBox Name="listBox1" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
</Grid>
</Expander>
<!-- SECOND EXPANDER THAT DOESN'T RELY ON A VIEWMODEL -->
<Expander Grid.Row="0" Grid.Column="1" Name="expander2" IsExpanded="True">
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding ElementName=listBox2, Path=Items.Count, UpdateSourceTrigger=PropertyChanged, StringFormat={}Items ({0})}" />
</StackPanel>
</Expander.Header>
<Grid>
<ListBox Name="listBox2" Width="Auto" Height="Auto" ItemsSource="{Binding Path=ListItems}" />
</Grid>
</Expander>
<Button Grid.Row="1" Grid.ColumnSpan="2" Content="Add an Item" Click="Button_Click" />
</Grid>
</Window>
Only Code Behind
If, for some reason, you are just using code-behind, you can do it this way:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
((INotifyCollectionChanged)listBox1.Items).CollectionChanged += new NotifyCollectionChangedEventHandler(ListBox_CollectionChanged);
}
void ListBox_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
expander1.Header = "Items (" + listBox1.Items.Count + ")";
}
}
I'm newbie on WPF and I would like to add images items to combobox which will read from hard disk according filter on it's name.
filter on images name should be binds to text property of different element.
Any advice?
Thanks!
It is a good example to show MVVM approach. Code below will do the job:
XAML
<Window x:Class="simplest.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:simplest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<TextBox x:Name="outputFolder" Height="30" Margin="5" Grid.Column="0" Text="{Binding Filter}"/>
<ComboBox Height="30" Grid.Column="1" Margin="5" ItemsSource="{Binding Images}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image MaxWidth="64" MaxHeight="64" Source="{Binding}" />
<TextBlock Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</Window>
C#
class MainWindowViewModel : INotifyPropertyChanged
{
private string _filter;
private ObservableCollection<string> _images = new ObservableCollection<string>();
private readonly string _filesSearchPath = "d:\\";
public MainWindowViewModel()
{
Filter = "*.jpg";
}
public string Filter
{
get
{
return _filter;
}
set
{
_filter = value;
OnPropertyChanged();
RefreshImagesCollection();
}
}
public ObservableCollection<string> Images
{
get
{
return _images;
}
}
private void RefreshImagesCollection()
{
_images.Clear();
foreach (var fileName in Directory.GetFiles(_filesSearchPath, _filter))
{
_images.Add(fileName);
}
}
private void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
ItemTemplate and ItemSource are the properties you need to set.
ItemTemplate should point to Datatemplate and ItemSource to Collection of strings(path to images).
This link will help you.
I am learning to use listBox in WPF with dataTemplate using the examples from MSDN, I can render a listBox bound to an ObservableCollection as a source and by overriding the ToString method.
However, I need to render an image and some texblocks for every item. Here's my XAML:
<Grid x:Class="MyAddin.WPFControls"
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:c="clr-namespace:MyAddin"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300"
Background="Transparent"
HorizontalAlignment="Stretch" Width="auto"
Height="215" VerticalAlignment="Stretch" ShowGridLines="False">
<Grid.Resources>
<c:People x:Key="MyFriends"/>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBlock HorizontalAlignment="Left"
IsManipulationEnabled="True"
Height="20" Width="300">Activity Feed</TextBlock>
<ListBox Grid.Row="1" Name="listBox1" IsSynchronizedWithCurrentItem="True"
BorderThickness="0" ScrollViewer.VerticalScrollBarVisibility="Auto"
VerticalContentAlignment="Stretch" Margin="0,0,0,5" Background="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Margin="5" BorderBrush="Black" BorderThickness="1">
<Image Source="{Binding Path=Avatar}" Stretch="Fill" Width="50" Height="50" />
</Border>
<StackPanel Grid.Column="1" Margin="5">
<StackPanel Orientation="Horizontal" TextBlock.FontWeight="Bold" >
<TextBlock Text="{Binding Path=Firstname }" />
</StackPanel>
<TextBlock Text="{Binding Path=Comment}" />
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
My Collection class is as following:
public class People : ObservableCollection<Person>
{ }
public class Person
{
private string firstName;
private string comment;
private Bitmap avatar;
public Person(string first, string comment, Bitmap avatar)
{
this.firstName = first;
this.comment = comment;
this.avatar = avatar;
}
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public string Comment
{
get { return comment; }
set { comment = value; }
}
public Bitmap Avatar
{
get { return avatar;}
set { avatar = value; }
}
public override string ToString()
{
return firstName.ToString();
}
}
Once my addin is loaded, I am downloading my data and setting the itemsSource.
People p = new People();
p.Add(new Person("Willa", "Some Comment", myAvatar));
p.Add(new Person("Isak", "Some Comment", myAvatar));
p.Add(new Person("Victor", "Some Comment", myAvatar));
this.wpfControl.listBox1.ItemsSource = p;
The problem I am facing is that the items are being rendered as empty rows whereas If I remove the dataTemplate, the items are rendered fine with their firstName.
Don't see anything wrong with the bindings themselves, but your avatar type seems off, WPF expects ImageSource (i do not know if there is any implicit convertion between Bitmap and ImageSource, check for binding errors to find out).
I would like to have some of the properties in the custom user control to be available to the parent page. I created a small sample to illustrate what I am looking for.
I am trying to use MVVM pattern and all the binding mechanisms to achieve it.
USERCONTROL XAML
<UserControl x:Class="TestCustomUserControl.MyControls.UserNameControl"
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:TestCustomUserControl.ViewModels"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<local:UserNameViewModel x:Key="TheViewModel"/>
</UserControl.Resources>
<Grid x:Name="NameCtrlRoot" Background="White" DataContext="{StaticResource TheViewModel}">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name: "/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtFirstName" Text="{Binding FirstName, Mode=TwoWay}">
<i:Interaction.Behaviors>
<!-- This behavior updates the binding after a specified delay
instead of the user having to lose focus on the TextBox. -->
<local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
</i:Interaction.Behaviors>
</TextBox>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="txtLastName" Text="{Binding LastName, Mode=TwoWay}">
<i:Interaction.Behaviors>
<!-- This behavior updates the binding after a specified delay
instead of the user having to lose focus on the TextBox. -->
<local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
</i:Interaction.Behaviors>
</TextBox>
<TextBlock Grid.Row="3" Grid.Column="0" Text="fullname inside control:" />
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding FullName}" />
<Border Height="1" Background="Black" Grid.Column="2" Grid.Row="4" />
</Grid>
</StackPanel>
</Grid>
the above Usercontrol is binded to the following VIEWMODEL
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public class UserNameViewModel : BaseViewModel
{
private String _firstName;
public String FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyPropertyChanged("FirstName");
OnNameChange();
}
}
private String _lastName;
public String LastName
{
get { return _lastName; }
set
{
_lastName = value;
NotifyPropertyChanged("LastName");
OnNameChange();
}
}
private void OnNameChange()
{
FullName = String.Format("{0} {1}", FirstName, LastName);
}
public String _fullName;
public String FullName
{
get { return _fullName; }
set {
_fullName = value;
NotifyPropertyChanged("FullName");
}
}
}
Consumer Page that uses the above USERCONTROL
<navigation:Page xmlns:my="clr-namespace:TestCustomUserControl.MyControls" x:Class="TestCustomUserControl.Views.ConsumeName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="ConsumeName Page">
<Grid x:Name="LayoutRoot">
<StackPanel>
<my:UserNameControl x:Name="MyNameControl"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Full Name in Parent: " />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding FullName, ElementName=MyNameControl}"/>
</Grid>
</StackPanel>
</Grid>
Here is my question now, If you look at the view model associated with user control, it has a property called FullName and I would like that to be exposed via Usercontrol, so that I can access it from the consuming page. Its like consuming page want to access some of the properties of usercontrol. I am not quite sure as to how that can be acheived. I would like to stick with MVVM pattern.
you have already decalred a StaticResource, so you can use it in both views.
Do this
<UserControl.Resources>
<local:UserNameViewModel x:Key="TheViewModel"/>
</UserControl.Resources>
in the "ConsumeName Page". If yous simply add the DataContext to your Grid
<Grid x:Name="LayoutRoot" DataContext="{StaticResource TheViewModel}">
this should work. (You don't need ElementName=MyNameControl any longer).
The my:UserNameControl should inherit the DataContext. If not, you have to add it here.
<my:UserNameControl DataContext="{StaticResource TheViewModel}"/>
This should work.
Right now the local:UserNameViewModel with the key TheViewModel is only achievable where you defined it. If you define it in your app.xaml you can access it from everywhere in the app.
Hope this hels.
BR,
TJ
I am answering my own question. But before answering my question, just want to clarify somethings. I was trying to create a fully encapsulated UserControl with its own ViewModel. And where ever the usercontrol is consumed, the consumer should know nothing about the usercontrol's internal viewmodel. Only communication option I want the consumer to have is by setting some properties and using binding mechanism.
So the way I resolved my problem is, I created dependency property inside the UserControl and setting it whenever something changes in the usercontrol's viewmodel.
UserNameControl.xaml (usercontrol)
<UserControl x:Class="TestCustomUserControl.MyControls.UserNameControl"
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:TestCustomUserControl.ViewModels"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<local:UserNameViewModel x:Key="TheViewModel"/>
</UserControl.Resources>
<Grid x:Name="NameCtrlRoot" Background="White" DataContext="{StaticResource TheViewModel}">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:" />
<TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name: "/>
<TextBox Grid.Row="0" Grid.Column="1" x:Name="txtFirstName" Text="{Binding FirstName, Mode=TwoWay}">
<i:Interaction.Behaviors>
<!-- This behavior updates the binding after a specified delay
instead of the user having to lose focus on the TextBox. -->
<local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
</i:Interaction.Behaviors>
</TextBox>
<TextBox Grid.Row="1" Grid.Column="1" x:Name="txtLastName" Text="{Binding LastName, Mode=TwoWay}">
<i:Interaction.Behaviors>
<!-- This behavior updates the binding after a specified delay
instead of the user having to lose focus on the TextBox. -->
<local:TextChangedDelayedBindingBehavior RefreshTimeMilliseconds="750" />
</i:Interaction.Behaviors>
</TextBox>
<TextBlock Grid.Row="3" Grid.Column="0" Text="fullname inside control:" />
<TextBlock Grid.Row="3" Grid.Column="1" Text="{Binding FullName}" />
<Border Height="1" Background="Black" Grid.Column="2" Grid.Row="4" />
</Grid>
</StackPanel>
</Grid>
BaseViewModel.cs
public class BaseViewModel : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
#endregion
protected void NotifyPropertyChanged(string propName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
UserNameViewModel.cs
public class UserNameViewModel : BaseViewModel
{
private String _firstName;
public String FirstName
{
get { return _firstName; }
set
{
_firstName = value;
NotifyPropertyChanged("FirstName");
OnNameChange();
}
}
private String _lastName;
public String LastName
{
get { return _lastName; }
set
{
_lastName = value;
NotifyPropertyChanged("LastName");
OnNameChange();
}
}
public Action NameChanged { get; set; }
private void OnNameChange()
{
FullName = String.Format("{0} {1}", FirstName, LastName);
if(NameChanged != null) NameChanged.Invoke();
}
private String _fullName;
public String FullName
{
get { return _fullName; }
set {
_fullName = value;
NotifyPropertyChanged("FullName");
}
}
}
UserNameControl.xaml.cs (Usercontrol code behind with DependencyProperty declaration)
public partial class UserNameControl : UserControl
{
private UserNameViewModel _TheViewModel;
public UserNameControl()
{
InitializeComponent();
_TheViewModel = Resources["TheViewModel"] as UserNameViewModel;
_TheViewModel.NameChanged = OnNameChanged;
}
public String SelectedFullName
{
get { return (String) GetValue(SelectedFullNameProperty); }
set { SetValue(SelectedFullNameProperty, value); }
}
public static readonly DependencyProperty SelectedFullNameProperty =
DependencyProperty.Register("SelectedFullName", typeof (String), typeof (UserNameControl), null);
private void OnNameChanged()
{
SelectedFullName = _TheViewModel.FullName;
}
}
ConsumeName.xaml (Consumer navigation Page, user above usercontrol and pulls SelectedFullName into UserFullName)
<navigation:Page xmlns:my="clr-namespace:TestCustomUserControl.MyControls" x:Class="TestCustomUserControl.Views.ConsumeName"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:local="clr-namespace:TestCustomUserControl.ViewModels"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="640" d:DesignHeight="480"
Title="ConsumeName Page">
<navigation:Page.Resources>
<local:ConsumeNameViewModel x:Key="TheConsumeNameViewModel"/>
</navigation:Page.Resources>
<Grid x:Name="LayoutRoot" DataContext="{StaticResource TheConsumeNameViewModel}">
<StackPanel>
<my:UserNameControl x:Name="MyNameControl" SelectedFullName="{Binding UserFullName, Mode=TwoWay}" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Full Name in Parent: " />
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding UserFullName}"/>
</Grid>
</StackPanel>
</Grid>
ConsumeNameViewModel.cs
public class ConsumeNameViewModel : BaseViewModel
{
private string _UserFullName;
public string UserFullName
{
get { return _UserFullName; }
set
{
if (value != _UserFullName)
{
_UserFullName = value;
NotifyPropertyChanged("UserFullName");
}
}
}
}
I'm trying to raise validation errors:
1. when the user enter a US MSRP that is not a number, silverlight displays an error when leaving the field
2. However if in the same field I put a negative number, no error is displayed even though there is an explicit range specified
What do I need to change?
Also, as a bonus question, what am I supposed to use in the XAML to read the value Display(Name = "My Name is US MSRP:" rather than explicitly specifying something else
public class CalculatorParameters : INotifyPropertyChanged
{
private double _usMsrp;
public CalculatorParameters()
{
}
[Display(Name = "My Name is US MSRP:",
Description = "The residual value is based on the US MSRP, even with Euro-Delivery")]
[Range(0, 150000, ErrorMessage = "US MSRP must be a positive amount under $150,000")]
public double UsMsrp
{
get { return _usMsrp; }
set
{
_usMsrp = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("UsMsrp"));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
And here is the XAML
<UserControl x:Class="Silverlight.ConfigEnhanced.UcFinance"
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:my="clr-namespace:Silverlight.ConfigEnhanced"
xmlns:df="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="650" d:DesignWidth="500" >
<UserControl.Resources>
<my:CalculatorParameters x:Key="descriptor"/>
</UserControl.Resources>
<df:DataForm x:Name="df1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CurrentItem="{StaticResource descriptor}" CommandButtonsVisibility="None" AutoGenerateFields="False" >
<df:DataForm.EditTemplate>
<DataTemplate>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="160"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="270"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="2" Grid.Column="0">
<TextBlock Text="Leasing" HorizontalAlignment="Center" VerticalAlignment="Center" FontWeight="Bold" Margin="0,0,0,15" />
<df:DataField Label="Term in Months">
<TextBox Name="txtBoxLeaseTermInMonths" Text="{Binding LeaseTermInMonths, Mode=TwoWay}" GotFocus="RecomputeLease"/>
</df:DataField>
<df:DataField Label="Down Payment">
<TextBox Name="txtBoxLeaseDownPayment" Text="{Binding LeaseDownPayment, Mode=TwoWay}" GotFocus="RecomputeLease"/>
</df:DataField>
<df:DataField Label="Money Factor">
<TextBox Name="txtBoxLeaseMoneyFactor" Text="{Binding MoneyFactor, Mode=TwoWay}" GotFocus="RecomputeLease"/>
</df:DataField>
<df:DataField Label="US MSRP">
<TextBox Name="txtBoxLeaseUsMsrp" Text="{Binding UsMsrp, Mode=TwoWay}" GotFocus="RecomputeLease"/>
</df:DataField>
</StackPanel>
</Grid>
</DataTemplate>
</df:DataForm.EditTemplate>
</df:DataForm>
</UserControl>
http://betaforums.silverlight.net/forums/p/113795/257670.aspx