Something is wrong with my binding. But I can't find it
I have a status type control (UserControl) that has an ItemsControl with binding that relies on a ViewModelBase object which provides list of BrokenRules, like so:
<ItemsControl ItemsSource="{Binding BrokenRules}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock>
<Hyperlink Foreground="Red" >
<TextBlock Text="{Binding Description}" />
</Hyperlink>
</TextBlock>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The binding works the way I want to, in the sense that any and all broken rules Descriptions are displayed. A rule is pretty much just a description and a delegate that is executed when the rule is told to validate itself.
Most rules have Descriptions that are known up front, before the rule is asked to validate itself. For example, "Name is not valued" is a fine description of what went wrong if the validation delegate !Name.IsNullOrEmptyAfterTrim() fails.
The problem comes with one particular rule, which checks for duplicate names. If the dup check fails, I want to be able to say what the duplicated value is, which is impossible to know up front. So the rule needs to update the Description when the validation delegate is executed.
When I unit test or leave a debug trace in the validation delegate, the broken rule description is updated. But when I run the app, the broken rule description is what is was before it was updated.
I am therefore guessing my binding is not correct. Can anyone suggest what the problem / fix is?
Cheers,
Berryl
UPDATE ====================
This is code from my ViewModelBase class:
private readonly List<RuleBase> _rules = new List<RuleBase>();
// inheritors add rules as part of construction
protected void _AddRule(RuleBase rule) { _rules.Add(rule); }
public ObservableCollection<RuleBase> BrokenRules { get { return _brokenRules; } }
protected ObservableCollection<RuleBase> _brokenRules;
public virtual IEnumerable<RuleBase> GetBrokenRules() {
return GetBrokenRules(string.Empty);
}
public virtual IEnumerable<RuleBase> GetBrokenRules(string property) {
property = property.CleanString();
_brokenRules = new ObservableCollection<RuleBase>();
foreach (var r in _rules) {
// Ensure we only validate this rule
if (r.PropertyName != property && property != string.Empty) continue;
var isRuleBroken = !r.ValidateRule(this);
if (isRuleBroken) _brokenRules.Add(r);
return _brokenRules;
}
You must ensure that the BrokenRules observable collection instance doesn't change, your code on the View Model should look something like:
public ObservableCollection<BrokenRule> BrokenRules
{
get;
set;
}
private void ValidateRules()
{
// Validation code
if (!rule.IsValid)
{
this.BrokenRules.Add(new BrokenRule { Description = "Duplicated name found" });
}
}
If for example, you do something like this instead:
this.BrokenRules = this.ValidateRules();
You would be changing the collection which is bound to the ItemsControl without notifying it and changes won't reflect on UI.
Related
I have a WPF application that is supposed to alert users about impending errors in a pump. There are three kinds of errors, which I have defined as an enum in a PumpErrorModel. This pumpErrorModel also knows which type of error is the most probable. This is exposed by my PumpErrorViewModel to the GUI, which has a label bound to the value of this most probable error, converted to a string. So far, so good.
// from MainWindow.xaml
<Label ... Content="{Binding LikelyError, Converter={StaticResource PumpErrorTypeToString}}" />
// from PumpErrorViewModel.cs
private PumpErrorModel.PumpErrorType likelyError;
public PumpErrorModel.PumpErrorType LikelyError {
get { return likelyError; }
private set { likelyError = value; RaisePropertyChanged("LikelyError"); } }
In my GUI I also have descriptions of each type of pump error, i.e one for each value of the enum. I would like to bind the background of each of these labels to the value of the most probable error type, so that when the most probable error type is "Explosions", then the label describing explosions has a red background, while the other labels have a white background.
// from MainWindow.xaml. I would like to bind the background of these
<Label Content="Likely Error: Explosions" />
<Label Content="Likely Error: More Explosions!" />
<Label Content="Likely Error: Rabbits!!!" />
I could make a boolean Property in the viewmodel for each type of error, that indicate if the probable error is of a specific type. I could then bind each of the label backgrounds to the corresponding properties. But it seems a bit messy to me, since I have to call RaisePropertyChanged on each of these extra in the setter of the original probable error property.
// proposed code in PumpErrorViewModel.cs
public bool IsRabbits { get { return LikelyError == PumpErrorModel.PumpErrorType.Rabbits; } };
// amended LikelyError setter
public PumpErrorModel.PumpErrorType LikelyError {
get ...
private set { likelyError = value;
RaisePropertyChanged("LikelyError");
RaisePropertyChanged("IsRabbits"); } }
// proposed code in MainWindow.xaml
<Label Content="Likely Error: Rabbits!!!" BackGround="{Binding IsRabbits, Converter){StaticResource BoolToColor}}" />
If I do this, then I have a coupling between LikelyError and IsRabbits, that I am likely to forget whenever I add new error types and boolean properties. Is there a better way to achieve my goal?
This seems like a good situation to use a child collection on your main view model. Each view model in that child collection would represent a single pump error type. Your view would then bind to that collection to list all potential pump errors and highlight the likely one. Here's our starting point for discussion below:
public class MainViewModel : ViewModel
{
private readonly ICollection<PumpErrorTypeViewModel> pumpErrorTypes;
public MainViewModel()
{
this.pumpErrorTypes = Enum.GetValues(typeof(PumpErrorType))
.Cast<PumpErrorType>()
.Select(x => new PumpErrorTypeViewModel(x))
.ToList();
}
pubilc ICollection<PumpErrorTypeViewModel> PumpErrorTypes
{
get { return this.pumpErrorTypes; }
}
public class PumpErrorTypeViewModel : ViewModel
{
private readonly PumpErrorType type;
public PumpErrorTypeViewModel(PumpErrorType type)
{
this.type = type;
}
public PumpErrorType Type
{
get { return this.type; }
}
public string Display
{
// do whatever formatting you like here
get { return string.Format("Likely Error: {0}", this.type); }
}
}
<ItemsControl ItemsSource="{Binding PumpErrorTypes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Display}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now, to make the leap to coloring the background of the labels, we have a number of ways we could achieve this but the two most common would be:
MainViewModel could have a LikelyError property. PumpErrorTypeViewModel could take MainViewModel in its constructor and expose a Background property. It could listen to changes in LikelyError and invalidate its Background accordingly. This is a good fit for a reactive implementation (see ReactiveUI).
PumpErrorViewModel could expose an IsLikely property which, when set, invalidates its Background property. MainViewModel can loop through all pumpErrorTypes whenever LikelyError changes and update the IsLikely property on the child.
Either way, the view has one simple change:
<Label Content="{Binding Display}" Background="{Binding Background}"/>
I want to implement two types of validation in my silverlight application. I want "business-logic" rules to be implemented in viewmodel(like end date is not earlier than start date) which i have already accomplished, and input validation somewhere on main control, where input fields are(like date is in bad format). Is there anything silverlight can "help" me with? I mean there is at least UnsetValue there for me, but is there any event associated or i have to catch all OnChanged events? Also is there a way to manually display red border around control when i want to?
Sorry, it was not obvious from my question, but i finished with the part that includes "business-logic" rules - my viewmodel indeed implements INotifyDataErrorInfo, i'm troubled with second type of validation.
Implement INotifyDataErrorInfo on your ViewModel to enable validation on View Model level.
Implement INotifyDataErrorInfo on your properties
then on your property that is binded in XAML use a friendly display name:
private DateTime? _datumP = DateTime.Now;
[Display(Name = "Date", ResourceType = typeof(CommonExpressions))]
public DateTime? DatumP
{
get
{
return _datumP;
}
set
{
if (_datumP != value)
{
_datumP = value;
RaisePropertyChanged(DatumPonudbePropertyName);
}
ValidateDate(DatumPonudbe, DatumPonudbePropertyName);
}
}
Then your method to validate dates:
public void ValidateDate(DateTime? value, string propertyName)
{
RemoveError(propertyName, CommonErrors.DatumNull_ERROR);
if (value == null)
AddError(propertyName, CommonErrors.DatumNull_ERROR, false);
}
And now for the XAML part:
<sdk:DatePicker Width="100" SelectedDate="{Binding DatumP, Mode=TwoWay,
NotifyOnValidationError=True, ValidatesOnNotifyDataErrors=True,
ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
P.S.
CommonExpressions and CommonErrors are my Resource files for multilanguage, you can use plain strings here.
I've been reading a lot about MVVM (using Laurent Bugnion's library in specific) and I'm constantly struggling to determine how to do things in MVVM that were otherwise easy with code behind.
Here's just one example where I suspect I'm doing things the hard way. If anyone has the time to read all this, perhaps they can comment on the sanity of my approach. :)
I have a list box bound to a ViewModel like so:
<ListBox x:Name="lstFruitBasketLeft" ItemsSource="{Binding FruitBasket}"
SelectedItem="{Binding SelectedFruit, Mode=TwoWay}" Width="150">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" VerticalAlignment="Center"
HorizontalAlignment="Left" Margin="2">
<TextBlock Text="{Binding Name}" />
<TextBlock Text=":" />
<TextBlock Text="{Binding Quantity}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
The ItemSource is an ObservableCollection of Fruit objects:
public class Fruit
{
public string Name { get; set; }
public int Quantity { get; set; }
public Fruit() { }
public Fruit(string name, int quantity)
{
this.Name = name;
this.Quantity = quantity;
}
}
It is defined in the ViewModel as:
// Property FruitBasket
public const string FruitBasketPropertyName = "FruitBasket";
private ObservableCollection<Fruit> _fruitBasket = null;
public ObservableCollection<Fruit> FruitBasket
{
get { return _fruitBasket; }
set
{
if (_fruitBasket == value)
return;
_fruitBasket = value;
// Update bindings, no broadcast
RaisePropertyChanged(FruitBasketPropertyName);
}
}
The bound SelectedItem property is as such:
//Property SelectedFruit
public const string SelectedFruitPropertyName = "SelectedFruit";
private Fruit _selectedFruit = null;
public Fruit SelectedFruit
{
get { return _selectedFruit; }
set
{
if (_selectedFruit == value)
return;
var oldValue = _selectedFruit;
_selectedFruit = value;
// Update bindings, no broadcast
RaisePropertyChanged(SelectedFruitPropertyName);
}
}
Then, the list is populated on the construction of the ViewModel.
Now, I add a RelayCommand to a button on the presentation page that executes a method which increments the quantity of the selected item. Note that I am not using the parameter yet, but "Bob" is a placeholder for some changes for later.
<Button x:Name="butMore" Content="More!" HorizontalAlignment="Right" Height="25" Width="75" Margin="4">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<cmd:EventToCommand
Command="{Binding addMoreCommand}"
CommandParameter="Bob" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Here's the code for the command:
// Property addMoreCommand
public RelayCommand addMoreCommand
{
get;
private set;
}
...
//Init relays (this is in the constructor)
addMoreCommand = new RelayCommand(AddFruit, CanExecute);
...
public void AddFruit()
{
//Increment the fruit
SelectedFruit.Quantity++;
//Save the previous selected item
Fruit oldSelectedItem = SelectedFruit;
//We have to have a new list in order to get the list box to refresh
FruitBasket = new ObservableCollection<Fruit>(FruitBasket);
//Reselect
SelectedFruit = oldSelectedItem;
}
public bool CanExecute()
{
return true; //for now
}
Now this does work, but I have some problems with it:
First, I feel like there are a lot of conditions that have to come together for this to work and I wonder if I'll get so lucky trying to move some Telerik Drag and Drop code into MVVM.
Second, it seems like a pretty poor performance approach to recreate the list like that.
Lastly, it seems like this would be easier in code behind (though I'm not 100% certain I still won't have to rebuild that list).
Does anyone have any thoughts on my approach or perhaps even... suggestions to make things easier? Am I just missing something obvious here?
Thanks
-Driodilate :]
maulkye,
There is something going wrong if you have to refresh your ObservableCollection. Usually, you should not need it because the ObservableCollection will notify about item changes.
Never do this:
FruitBasket = new ObservableCollection<Fruit>(FruitBasket);
Your public ObservableCollection<Fruit> FruitBasket should have no public setter, it should be read only. Just Add or Remove Items to/from the list.
If you want to handle multiple selections, you will probably need an extended CollectionView which can handle this, get more hints here.
I hope this helps a little bit, even if I probably didn't answer all questions : )
EDIT:
Ok, I guess i got some things wrong. Now i guess i fully understand what you're trying to accomplish. You are not getting notified when your property is changed, right? Well, for this reason, we've adapted "BindableLinq" in one of our projects, which you can compile in Silverlight without problems. (there are similar solutions available, called Continuous Linq or Obtics, make your choice).
Using BindableLinq, you can transform your ObservableCollection to a BindableCollection using one single extension method. The BindableCollection will then reflect all changes properly. Give it a try.
EDIT2:
To implement a proper ViewModel, Please consider the following Changes.
1) Fruit is your Model. Since it doesn't implement INotifyPropertyChanged, it won't propagate any changes. Create a FruitViewModel, embedding your Fruit Model and invoke RaisePropertyChanged for each property setter.
2) Change your FruitBasket to be an ObservableCollection of FruitViewModel. Slowly it starts to make sense :)
3) SelectedFruit has to be a FruitViewModel as well. Now it makes even more sense.
4) Now it already works for me, even without BindableLinq. Did you have any success?
HTH
best regards,
thomas
In C# (or in C# with WPF), how would I build a checkbox at run time?
I would I be able to query the check box to see if the user clicked on it?
In other words, suppose I have a "grid" on which I want to have displayed some checkboxes. But I do not know how many checkboxes to display. I suppose I could (in WPF) fill the grid with checkboxes at design time and mark them as hidden (or visibly == false) and then show them at run time. But I was hoping there was a more elegant way to do this.
There are several ways to do this in WPF. A quick and dirty approach would be to do something like this:
<StackPanel x:Name="CheckBoxes" />
Then in your code behind do:
for (int i=0; i < 10; i++) {
this.CheckBoxes.Children.Add(new CheckBox());
}
But while at first glance it looks simple, this makes it somewhat of a pain to work with in the long run. Instead, a better solution would be to have a class that has a boolean property such as:
// this should really implement INotifyPropertyChanged but
// we'll ignore that for now...
public class SelectableThing {
public bool IsSelected {
get;
set;
}
public string Description {
get;
set;
}
}
Then in your XAML, you would have a bindable control such as ItemsControl:
<ItemsControl x:Name="CheckBoxes">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, Mode=TwoWay}"
Content="{Binding Description}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Then in your code behind you could create a collection of these SelectableThing's and set them as the ItemsSource.
private SelectableThing[] things;
// where you do this is up to you really
private void Window_Load(object sender, RoutedEventArgs e) {
things = new SelectableThing[] {
new SelectableThing("First Thing"),
new SelectableThing("Second Thing"),
new SelectableThing("Third Thing")
};
CheckBoxes.ItemsSource = things;
}
In an event handler or something like that, eventually a method that gets called, you could do this. Let's say your Canvas is called myCanvas.
var cb = new CheckBox { //... set the properties, e.g.:
Checked = true, Content = "Check me" };
// do whatever you like to do with your newly created CheckBox
myCanvas.Children.Add(cb);
Hope this helps; of course you can do this inside a loop. If you need to hold a specific set of references to the created CheckBoxes be aware of that or use the Tag Property to identify these special CheckBoxes. Also, you could check myCanvas.Children for CheckBoxes.
I am binding my entities to an edit form in WPF. Within a DataTemplate, I want to be able to set the background color of the root container within a DataTemplate to show it has been changed and these changes have not yet been submitted to the database.
Here's a very simple sample that demonstrates what I'm talking about (forgive errors):
<Page ...>
<Page.DataContext>
<vm:MyPageViewModel /> <!-- Holds reference to the DataContext -->
</Page.DataContext>
<ItemsControl
ItemsSource = {Binding Items}>
<ItemsControl.Resources>
<DataTemplate
DataType="Lol.Models.Item"> <!-- Item is L2S entity -->
<!-- In real life, I use styles to set the background color -->
<TextBlock Text="{Binding IsDirty, StringFormat='Am I dirty? /{0/}'}"/>
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Page>
The example just prints out "Am I dirty? yes" or "Am I dirty? no", but you get the idea.
To do this, I'll need to add a public property to my Item (partial class, simple) that can determine if the entity is dirty or not. This is the tough bit.
public partial class Item
{
public bool IsDirty
{
get
{
throw new NotImplementedException("hurf durf");
}
}
}
Outside of the entity, it's pretty simple (as long as you have the DataContext the entity is attached to). Inside, not so much.
What are my options here?
Edit: I don't think there's one good solution here, so suggestions for workarounds are welcome.
(Okay, similar questions exist, but they are all about how to determine this from outside of the entity itself and use the DataContext the entity is attached to.)
If you are using the dbml generated classes, you should be able to implement a couple of partial methods like this:
public partial class SampleEntity
{
partial void OnCreated()
{
this.IsDirty = true;
}
partial void OnLoaded()
{
this.PropertyChanged += (s, e) => this.IsDirty = true;
this.IsDirty = false;
}
public bool IsDirty { get; private set; }
}