I'm using template selector for data grid in WPF.
I've got this code:
<l:ProblemTemplateSelector x:Key="problemTemplateSelector">
<l:ProblemTemplateSelector.ArithmeticTemplate>
<DataTemplate>
<Grid Background="LightBlue">
<l:ArithmeticUserControl Problem="{Binding ElementName=this}" />
</Grid>
</DataTemplate>
</l:ProblemTemplateSelector.ArithmeticTemplate>
</l:ProblemTemplateSelector>
In the usercontrol, I need to set in Problem property all the element, but I don't know what I'm doing wrong. Thanks.
UPDATE 1:
I'm gonna be more specific if you need details, I've got a class Exercise where is a user control:
public class Exercise : UserControl
{
public static readonly DependencyProperty ProblemProperty = DependencyProperty.Register(
"Problem", typeof(Problem), typeof(Exercise), new PropertyMetadata(null, ));
public virtual Problem Problem
{
get
{
return (Problem)GetValue(ProblemProperty);
}
set
{
SetValue(ProblemProperty, value);
}
}
}
This I've got another class where is derived from Exercise.
public partial class ArithmeticUserControl : Exercise
{
public ArithmeticUserControl()
{
InitializeComponent();
}
public override Problem Problem
{
get
{
return base.Problem;
}
set
{
base.Problem = value;
Arithmetic p = (Arithmetic)value;
Number1 = p.Number1;
Number2 = p.Number2;
Operator = p.Operation;
}
}
}
You should just be able to declare the binding as "{Binding}" and have it bind the Problem property to the data item.
EDIT: After seeing your example, you need to change how you're handling the Problem property. The WPF binding system does not make use of CLR properties, so the CLR properties exist only as a convenience for user code. Because of this, the code in your property is never being executed when the value is set through binding.
Instead, you need to handle property value changes by overriding OnPropertyChanged. For example, in your inherited class:
protected override OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
if(e.Property == ProblemProperty)
{
Arithmetic p = (Arithmetic)Problem;
Number1 = p.Number1;
Number2 = p.Number2;
Operator = p.Operation;
}
}
Simply use a {Binding} to pass a Data Item in the control.
If this won't help, along with your control add a TextBlock and see what is binds through straight binding:
<Grid Background="LightBlue">
<l:ArithmeticUserControl Problem="{Binding ElementName=this}" />
<TextBlock Text="{Binding}" Background="Green" ></TextBlock>
</Grid>
So on green background you shoudl see what is came to the ListBoxItem
Related
I'm working on a "simple" case. I like to create a new custom control which implements a DependencyProperty. In the next step I like to create a binding for updating the properties in both directions. I've builded a simple sample for this case, but the binding doesn't seem to work. I've found a way for updating the DPControl's property by using the FrameworkPropertyMetadata, but I don't know whether it's also a good idea to use the OnPropertyChanged event.
HERE is my sample project:
My control contains simply a Label
<UserControl x:Class="WPF_MVVM_ListBoxMultiSelection.DPControl"
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:WPF_MVVM_ListBoxMultiSelection"
mc:Ignorable="d" Height="84.062" Width="159.641">
<Grid Margin="0,0,229,268">
<Label Content="TEST" x:Name="label" Margin="0,0,-221,-102"/>
</Grid>
</UserControl>
and implement a custom dependency property. Currently, I have also implemented the PropertyChanged method for the FramePropertyMetadata and set in this method the label's content, but I like to get it work in both directions.
public partial class DPControl : UserControl
{
public DPControl()
{
InitializeComponent();
}
public string MyCustomLabelContent
{
get { return (string)GetValue(MyCustomLabelContentProperty);}
set
{
SetValue(MyCustomLabelContentProperty, value);
}
}
private static void OnMyCustomLabelContentPropertyChanged(DependencyObject source,
DependencyPropertyChangedEventArgs e)
{
DPControl control = (DPControl)source;
control.label.Content = e.NewValue;
}
public static readonly DependencyProperty MyCustomLabelContentProperty = DependencyProperty.Register(
"MyCustomLabelContent",
typeof(string),
typeof(DPControl),
new FrameworkPropertyMetadata(null,
OnMyCustomLabelContentPropertyChanged
)
);
I use this control simply in a Window by:
<local:DPControl MyCustomLabelContent="{Binding MyLabelContent, Mode=TwoWay}" Margin="72,201,286,34"/>
MyLabelContent is a property in the ViewModel, which has implemented also the INotifyPropertyChanged interface.
public class ViewModel_MainWindow:NotifyPropertyChanged
{
private string _myLabelContent;
public string MyLabelContent
{
get { return _myLabelContent; }
set { _myLabelContent = value;
RaisePropertyChanged();
}
}...
So how can I get it work: Using the binding feature with my new control on custom properties.
In your UserControl:
<Label
Content="{Binding MyCustomLabelContent, RelativeSource={RelativeSource AncestorType=UserControl}}"
x:Name="label" Margin="0,0,-221,-102"/>
And get rid of that property-changed callback. All you need is the Binding.
I like to get it work in both directions
To make the dependency property two-way by default:
public static readonly DependencyProperty MyCustomLabelContentProperty =
DependencyProperty.Register(
"MyCustomLabelContent",
typeof(string),
typeof(DPControl),
new FrameworkPropertyMetadata(null,
FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
I omitted the unnecessary property change handler.
It can't usefully be two-way now, because Label.Content can't generate its own value. If you want your UserControl to set the value in its codebehind, that's easy:
MyCustomLabelContent = "Some arbitrary value";
If you did the binding like I showed you, that will update the Label in the UserControl XAML as well as the viewmodel property bound to the UserControl's dependency property.
If you want the XAML to set it, you'll need to
Lastly, this:
Margin="0,0,-221,-102"
Is not a good way to do layout. WPF layout with Grid, StackPanel, etc. is much easier and more robust.
I have three projects in my solution:
My main WPF Application which contains a MainWindow + MainViewModel
UserControl Library with a UserControl (ConfigEditorView)
UIProcess class with the ViewModel for the UserControl (ConfigEditorViewModel)
In my MainWindow I want to use the UserControl with the ViewModel of UIProcess.
First I set the UserControl in my MainWindow:
<TabItem Header="Editor">
<Grid>
<cel:ConfigEditorView DataContext="{Binding ConfEditModel, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</TabItem>
I don't know which of these properties I need here, so I put all together but it still doesn't work.
Then I've set this in my MainViewModel:
public ConfigEditorViewModel ConfEditModel { get; set; }
With simple method that is bound to a Button:
private void doSomething()
{
ConfEditModel = new ConfigEditorViewModel("Hello World");
}
My ConfigEditorViewModel looks basically like this:
public class ConfigEditorViewModel : ViewModelBase
{
private string _Description;
public string Description
{
get
{
return _Description;
}
set
{
_Description = value;
base.RaisePropertyChanged();
}
}
public ConfigEditorViewModel(string t)
{
Description = t;
}
}
The description is bound to a TextBox in my UserControl.
<TextBox Grid.Row="1" Grid.Column="1" Margin="0,0,0,10" Text="{Binding Description}"/>
When I start the application and click the Button the TextBox should contain "Hello World" but it's empty.
What I've done wrong?
i gave you a general answer:
within a "real(a usercontrol you wanna use with different viewmodels with different property names)" usercontrol you bind just to your own DependencyProperties and you do that with ElementName or RelativeSource binding and you should never set the DataContext within a UserControl.
<UserControl x:Name="myRealUC" x:class="MyUserControl">
<TextBox Text="{Binding ElementName=myRealUC, Path=MyOwnDPIDeclaredInMyUc, Path=TwoWay}"/>
<UserControl>
if you do that you can easily use this Usercontrol in any view like:
<myControls:MyUserControl MyOwnDPIDeclaredInMyUc="{Binding MyPropertyInMyViewmodel}"/>
and for completeness: the Dependency Property
public readonly static DependencyProperty MyOwnDPIDeclaredInMyUcProperty = DependencyProperty.Register(
"MyOwnDPIDeclaredInMyUc", typeof(string), typeof(MyUserControl), new PropertyMetadata(""));
public bool MyOwnDPIDeclaredInMyUc
{
get { return (string)GetValue(MyOwnDPIDeclaredInMyUcProperty); }
set { SetValue(MyOwnDPIDeclaredInMyUcProperty, value); }
}
Your view models (and, optionally, models) need to implement INotifyPropertyChanged.
Binding's aren't magic. There is no inbuilt mechanism that allows for code to be notified when a plain old property's value changes. You'd have to poll it in order to check to see if a change happened, which would be very bad, performance-wise.
So bindings will look at the objects they are bound against and see if they implement INotifyPropertyChanged and, if so, will subscribe to the PropertyChanged event. That way, when you change a property and fire the event, the binding is notified and updates the UI.
Be warned, you must implement the interface and use it correctly. This example says it's for 2010, but it works fine.
I am developing windows phone app .In app,I want to put multiple check box.I able to put multiple check box.But when i checked on check box i want getting its content(check box content).For that i am use checked event and also click event but i cant get result as i want.My xaml code is as below:
<ListBox Name="hobbylist" ItemsSource="{Binding}" Margin="0,0,10,10" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="hobbycheck" Content="{Binding Path=Title}"
IsChecked="{Binding IsSelected}" ClickMode="Release"
Click="CheckBox_Click" ></CheckBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Please help me ...
I think you are using the Checkbox not correctly according to its purpose.
Checkbox should represent a state (e.g. yes/no) regarding a subject. Still, you just have to use the Checked event when the checkbox gets checked and Unchecked otherwise.
So in the Checked event, get the content you wish.
Edit
You have to maintain this with the MVVM pattern somehow. For that, there are plenty of examples in the internet, I am sure you can handle that.
Instead of having Click="CheckBox_Click", use the Check event :
private void CheckBox_Checked (Object sender, EventArgs e)
{
var currentCheckBoxItem = sender as CheckBox;
if (currentCheckBoxItem.IsChecked == true)
{
//you manipulation here...
}
}
Still. this might just not work, because you haven't provided enough details of your matter.
Edit 2 A little of MVVM...
First, make a Hobby model class, with a single string property (you might change your mind later to add more properties, Idk) :
public class Hobby : INotifyPropertyChanged
{
private string _name;
public string Name
{
get
{
return _name;
}
set
{
_name = value;
OnPropertyChanged();
}
}
private bool _isSelected;
public bool IsSelected
{
get
{
return _isSelected;
}
set
{
_isSelected = value;
OnPropertyChanged();
}
}
//You can add some multiple properties here (***)
public Hobby (string hobbyName, bool isSelected)
{
Name = hobbyName;
IsSelected = isSelected;
}
//INotifiyPropertyChanged interface member implementation ...
}
(* ) For example, a short description and then bind it on the View. The major advantage of this MVVM pattern is logic separation, so if something has to change, the separation of each component makes it easier.
Second, create a ViewModel class (you should implement INotifyPropertyChanged interface) :
public class HobbiesViewModel : INotifyPropertyChanged
{
private ObservableCollection<Hobby> _hobbies;
public ObservableCollection<Hobby> HobbiesCollection
{
get
{
return _hobbies;
}
set
{
_hobbies = value;
OnPropertyChanged();
}
}
//Constructor
public HobbiesViewModel
{
HobbiesCollection = new ObservableCollection<Hobby>();
}
//INotifyPropertyChanged interface member implementation ...
}
Third, create an instance of the ViewModel (the ObservableCollection). Use this quick help out : In the App.xaml.cs, create a static object and use it through the app as you need it :
public partial class App
{
//This already exists in your app's code, but I've written it to
//make an idea where to write the Hobbies object
public static PhoneApplicationFrame RootFrame { get; private set; }
public static HobbiesViewModel Hobbies;
//Again, the already existing constructor
public App()
{
...
Hobbies = new HobbiesViewModel();
}
Now, you almost have it all set; You have the Model, you have the ViewModel, all that's left is to create the connection with the View. This can be easily done through binding. The ViewModel represents the DataContext of your control (in your case the LongListSelector, so in that View's (Page's) constructor, write the following statement :
yourListControlName.DataContext = App.Hobbies;
Now the binding is the only thing left. This is done in XAML code. I won't put a whole chunk of XAML code here, cause you know best how your control looks like. Still, judging by the short sample you provided, there a few adjustments only :
The items source of the list XAML control will be bound to the ObservableCollection object name of the ViewModel representing the control's DataContext. A bit fuzzy, huh? To be clearer, in this case, you need to write ItemsSource="{Binding HobbiesCollection}", the ObservableCollection. Also, in the template, you should have that CheckBox which is bound on your Model's properties :
<DataTemplate>
<StackPanel Orientation="Horizontal"> //StackPanel is kinda useless if you have
//only one child control in it. But I wrote
//according to your code.
<Checkbox Content="{Binding Name}" IsChecked="{Binding IsSelected, Mode=TwoWay}"/>
</StackPanel>
</DataTemplate>
Now, here things are a bit unclear to me. Why would you use the Checkbox? I've thought of the next possible scenario : You come with some string of your hobbies through deserialization of the Json Data. To add them to the ViewModel, you need only :
App.Hobbies.HobbiesCollection.Add(new Hobby("firstHobbyFromJson", true));
App.Hobbies.HobbiesCollection.Add(new Hobby("secondHobbyFromJson", true));
This would make all hobbies already selected in the View. I guess, you would add some other hobbies, the user doesn't have which are not selected and could add them now :
App.Hobbies.HobbiesCollection.Add(new Hobby("aNewHobby", false));
App.Hobbies.HobbiesCollection.Add(new Hobby("anotherNewHobby", false));
At this point, the user has all its previous hobbies in the list and as well some new hobbies you provided him. After his selection is done, if you need to serialize the Json with only the selected hobbies, you could get like this :
var userHobbies = App.Hobbies.HobbiesCollection.Where(h => h.IsSelected);
or with a foreach and get only those hobby objects which have the IsSelected property as true.
Good luck!
I found a Simpler solution.
My Model
You need to use two variables otherwise you may get an 'stackoverflowexception'
public class ModelObj
{
public int position { set; get; }
public bool isChecked
{
get { return IsChecked; }
set { IsChecked = value; }
}
public bool IsChecked;
}
Code to be added in xaml:
isChecked in xaml sets the ListView Checkbox
Mode=TwoWay updates the isChecked boolean value of the model class
<CheckBox IsChecked="{Binding isChecked , Mode=TwoWay}" Checked="checkBox_Checked" >
c# Code that handles the event
private void checkBox_Checked(object sender, RoutedEventArgs e)
{
foreach (ModelObj obj in listItem)
{
if (obj.isChecked == true)
{
int selectedPosition = obj.position;
}
}
}
First of all i will try to explain what i am doing. I am trying to draw a chess board. I have a user controll for cell
<Grid x:Name="LayoutRoot">
<Border BorderThickness="0" Margin="0" Background="{Binding CellColor, ElementName=userControl, Mode=TwoWay}"/>
<Border x:Name="ValidMoveMarker" BorderThickness="0" Margin="0" Background="#FFC1CAB4" Opacity="0"/>
<Image x:Name="img" Source="{Binding source, ElementName=userControl, Mode=TwoWay}" Cursor="Hand"/>
In code behind of this CellControl i have 2 dpProperties
public eColor? PieceColor
{
get { return (eColor?)GetValue(PieceColorProperty); }
set { SetValue(PieceColorProperty, value);}
}
public static readonly DependencyProperty PieceColorProperty = DependencyProperty.Register("PieceColor", typeof(eColor?), typeof(CellControl), null);
public eType? PieceType
{
get { return (eType?)GetValue(PieceTypeProperty); }
set { SetValue(PieceTypeProperty, value);}
}
public static readonly DependencyProperty PieceTypeProperty = DependencyProperty.Register("PieceType", typeof(eType?), typeof(CellControl), null);
where eColor and eType are enumerators. Here I also have one property
public ImageSource source
{
get
{
if (PieceColor == eColor.White)
{
switch (PieceType)
{
case eType.Pawn:
return new BitmapImage(new Uri("/PO.PC;component/Images/chess_piece_white_pawn_T.png", UriKind.Relative));
case eType.Knight:
return new BitmapImage(new Uri("/PO.PC;component/Images/chess_piece_white_knight_T.png", UriKind.Relative));
...
default:
return null;
}
}
else
{
switch (PieceType)
{
case eType.Pawn:
}
}
}
Now problem is when i try to use the control like this
<PP_Controls:CellControl PieceType="{Binding type, Mode=TwoWay}" PieceColor="{Binding color, Mode=TwoWay}"
where
private eColor? _color;
public eColor? color
{
get { return _color; }
set
{
_color = value;
OnPropertyChanged("color");
}
}
private eType? _type;
public eType? type
{
get { return _type; }
set
{
_type = value;
OnPropertyChanged("type");
}
}
nothings happens. But if i use control like this
<PP_Controls:CellControl PieceType="Bishop" PieceColor="Black"
it is working perfectly. Am I missing something in my bindings? Is this because "source" property is not dependency property itself? How can I workaround my problem?
Your target properties are dependency properties, and the source properties are CLR properties implementing INotifyPropertyChanged, so your binding {Binding type} etc should work - assuming that the DataContext to your "use with binding" is the type with the color/type properties. You should be able to tell if these bindings are failing by looking at the Output window when you run your application under the debugger (in Silverlight 5 you could also use a binding breakpoint, or otherwise you can apply a trivial ValueConverter to the binding to set a breakpoint for debugging).
However your control's source property depends on two other properties in a 'lazy' fashion. You are binding to the source property, but nothing will cause this binding to update when the property's computed value changes. You should add a dependency property changed handler to PieceColor and PieceType which calls OnPropertyChanged("source") (or equivalently convert it to a DP or notifying property, and explicitly recompute the value).
INTRODUCTION
I have created a DecimalTextBox UserControl that houses some decimal validation I need done, so that I dont need to recreate the validation each time, and can just use the UserControl instead. This validation has properties that need to be bound to, and so I have created DependencyProperties so I can bind to them, according to this article by Josh Smith.
THE PROBLEM
The control's validation is behaving strangely. When I type an erroneous value into the TextBox, it shows up as an error. However when I try to change the value back in the code the value shown in the textbox remains unchanged.
Here are the steps I perform that cause this error (in this example 1 is an invalid value):
Load the form and the default value is 0.
Enter 1 into the textbox (and the textbox goes red due to the validation result being and error)
In the code I set the property bound to the textbox to 0
The form still displays 1 in a red textbox
CODE EXAMPLE
I prepaired an example demonstrating the problem, which can be downloaded here.
I'll post some of the code here, if you want more let me know.
ValidationTestControl's XAML
<UserControl x:Class="WPFTestProject.ValidationTestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="clr-namespace:WPFTestProject"
x:Name="ValidationTest"
Height="50" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="Type 'Banana' here: "></TextBlock>
<TextBox MinWidth="100">
<TextBox.Text>
<Binding ElementName="ValidationTest" Path="Text" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" ValidatesOnDataErrors="True" ValidatesOnExceptions="True">
<Binding.ValidationRules>
<v:NotBananaValidationRule>
<v:NotBananaValidationRule.NotWhatBinding>
<v:NotBananaBinding x:Name="NotBananaValidationBinding"></v:NotBananaBinding>
</v:NotBananaValidationRule.NotWhatBinding>
</v:NotBananaValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock Text=" (the text will give error when = 'Banana')"></TextBlock>
</StackPanel>
</Grid>
ValidationTestControls Code Behind
(yes I know not very MVVM but I felt it was ok for this stand alone control)
public partial class ValidationTestControl : UserControl
{
public ValidationTestControl()
{
InitializeComponent();
Banana = "Banana";
Binding BananaBinding = new Binding("Banana");
BananaBinding.Source = this;
NotBananaValidationBinding.SetBinding(NotBananaBinding.NotWhatProperty, BananaBinding);
}
public static DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(ValidationTestControl), new PropertyMetadata());
public static DependencyProperty BananaProperty = DependencyProperty.Register("Banana", typeof(string), typeof(ValidationTestControl), new PropertyMetadata());
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
public string Banana
{
get
{
return (string)GetValue(BananaProperty);
}
set
{
SetValue(BananaProperty, value);
}
}
}
ValidationRule and FrameWorkElement created for binding
public class NotBananaValidationRule:ValidationRule
{
private NotBananaBinding _notWhatBinding;
public NotBananaBinding NotWhatBinding
{
get { return _notWhatBinding; }
set { _notWhatBinding = value; }
}
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
string what = value.ToString();
if(what == _notWhatBinding.NotWhat||string.IsNullOrEmpty(what))
return new ValidationResult(false,
"Please enter a string that is not " + _notWhatBinding.NotWhat);
else
return new ValidationResult(true, null);
}
}
public class NotBananaBinding : FrameworkElement
{
public static readonly DependencyProperty NotWhatProperty = DependencyProperty.Register(
"NotWhat", typeof(string), typeof(NotBananaBinding), new UIPropertyMetadata());
public string NotWhat
{
get { return (string)GetValue(NotWhatProperty); }
set { SetValue(NotWhatProperty, value); }
}
public NotBananaBinding() { }
}
Basically what this code does is check if you have typed "Banana" and then returns a validation error. The control exposes dependency properties because I want to be able to bind to them when I use the control. The FrameworkElement NotBananaBinding lets me create dependency properties (because it is a DependencyObject so i can bind stuff for the validation. The ValidationRule has a NotBananaBinding property that stores the dependency property and uses it in the validate method.
I know my property names are kinda crappy, sorry. The thing is that the example does a good job of displaying the error. In my haste to make an example I didn't name the variables very well. If you find the code crappy please download the sample here.
WHAT I'VE FIGURED OUT SO FAR
Basically this problem seems to be caused by the fact that I am not actually changing the value.
Even if I call OnPropertyChanged on the property, because the value is not different it doesn't try and reevaluate the Validation.
I can obviously change the value to some arbitrary Valid value and then change it to the one I want it to be and it will work, but I was hoping there is some way to get call validation manually, to reevaluate the value and then change it etc. The changing it away and back is kinda messy.
CONCLUSION
Am I doing something wrong (perhaps something about the way I implemented the validation and binding from Josh Smiths post)
Is this just a c# bug, or is the behavior intended? If so then why?
Are there any elegant ways to fix it?
u_u
The validation prevents the Text property to be set. Put a break point on the setter and you will see that it will not break when you type the last 'a'. If you type Bananan and hit Backspace and it errors, push the button and it will work. The validation makes sure that there can be no invalid value in your property. So if you save it to let's say a database while in error, it won't save an invalid value.