Array value binding in WPF - c#

I have an object with an Array of doubles property like this:
private double[] _axes;
public double[] Axes
{
get
{
return _axes;
}
set
{
_axes = value;
if (PropertyChanged != null)
{
NotifyPropertyChanged("Axes[]");
}
}
}
This Array is always assigned as a whole, because i get an Array of double out of my database object and set it to the Axes property like this:
object.Axes = GetDataRow(i);
(this is just to demonstrate that i in fact assign the whole Array)
In my XAML now i bind it like this:
<Label Content="{Binding Path=Axes[0], UpdateSourceTrigger=PropertyChanged}"
I set the DataContext of the window to the Object that has the Axes property and the UI receives one value from it at program start that i assign in the constructor of the object.
However, even though the "Axes[]" event is raised constantly (which i checked from another object), no further values arrive in the UI.
So my question is: CAN this even work? Does Axes[] not correspond to Axes[0]? what could be the problem?

Your property name is incorrect in the notification
if (PropertyChanged != null)
{
NotifyPropertyChanged("Axes");
}
Remove the brackets. This should solve your problem in this case. However, it is recommended to use ObservableCollection for binding a list of objects.

The binding system won't look "into" the array for updates. You just need to notify it that the property itself changed. You're currently saying that a property called Axes[] changed, but you don't have a property with that name. It's just Axes
public double[] Axes
{
get
{
return _axes;
}
set
{
_axes = value;
if (PropertyChanged != null)
{
NotifyPropertyChanged("Axes");
}
}
}
If you really need to know if an individual item in the array is changing, then you should be using ObservableCollection, which the binding system does know how to look "into".

No.
You have no property named Axes[], so that event has no effect.
You should not bind to arrays; instead, use ObservableCollection, and make the property read-only.

Related

Is it better to put my logic in an event handler or in a setter for MVVM (Xamarin `Picker` `SelectedItem` quirks)

SOLUTION IS IN EDIT OF THE ACCEPTED ANSWER
I have a view in which has two Pickers, I need to have it so that when the SelectedItem property in one Picker changes, the list of Items in the second Picker (ItemSource) changes as well.
Currently I have a bound the SelectedItem and SelectedIndex properties of both pickers to properties in my ViewModel. In the setter(s) for both of them, I perform the logic needed to change the list of Items in the second picker. The list of Items in the second picker changes successfully, but when I set the SelectedIndex (to make it select an Item by default), this fails if the index which I am setting it to is the same as the index which it was on in the previous list. It just shows the Title of the Picker instead, this issue seems to be related to this bug.
My Code:
Both Pickers are bound to an Observable collection of strings FYI
FrameType and DirectionType are Enums
I initially used only the SelectedItem property
Relevant XAML
<Label Grid.Row="1" Grid.Column="0" VerticalTextAlignment="Center"
Text="Direction: " />
<Picker x:Name="PickerDirection" Grid.Row="1" Grid.Column="1"
Title="Select Direction"
ItemsSource="{Binding Directions}"
SelectedItem="{Binding SelectedDirection}"
SelectedIndex="{Binding SelectedDirectionIndex}"></Picker>
<Label Grid.Row="2" Grid.Column="0" VerticalTextAlignment="Center"
Text="Frame: "/>
<Picker x:Name="PickerFrame" Grid.Row="2" Grid.Column="1"
Title="Select Frame"
ItemsSource="{Binding Frames}"
SelectedItem="{Binding SelectedFrame}"
SelectedIndex="{Binding SelectedFrameIndex}"></Picker>
Relevant View Model code:
public string SelectedDirection
{
get { return _selectedDirection; }
set
{
if (Directions.Contains(value))
{
if (_selectedDirection != value)
{
if (EnumUtils.ToEnumFromString<FrameType>(SelectedFrame) == FrameType.Road &&
!DirectionsRoad.Contains(value))
{
_selectedDirection = Directions[Directions.IndexOf(DirectionType.Right.ToString())];
}
else
{
_selectedDirection = value;
}
OnPropertyChanged();
}
}
}
}
public string SelectedFrame
{
get { return _selectedFrame; }
set
{
if (_selectedFrame != value)
{
_selectedFrame = value;
if (EnumUtils.ToEnumFromString<FrameType>(_selectedFrame) == FrameType.Road)
{
Directions = DirectionsRoad;
if (Directions.Contains(SelectedDirection))
{
SelectedDirectionIndex = Directions.IndexOf(SelectedDirection);
}
else
{
SelectedDirectionIndex = Directions.IndexOf(DirectionType.Right.ToString());
}
}else if (EnumUtils.ToEnumFromString<FrameType>(_selectedFrame) == FrameType.Lane)
{
Directions = DirectionsAll;
SelectedDirectionIndex = Directions.IndexOf(SelectedDirection);
}
OnPropertyChanged();
}
}
}
}
public int SelectedDirectionIndex
{
get { return _selectedDirectionIndex; }
set
{
if (_selectedDirectionIndex != value)
{
if (!(value < 0 || value >= Directions.Count))
{
_selectedDirectionIndex = value;
OnPropertyChanged();
}
}
}
}
public int SelectedFrameIndex
{
get { return _selectedFrameIndex; }
set
{
if (_selectedFrameIndex != value)
{
if (!(value < 0 || value >= Frames.Count))
{
_selectedFrameIndex = value;
OnPropertyChanged();
}
}
}
}
The outcome:
I expect it to never be empty since I ensure that the SelectedDirection is always set to something.
Notes:
I initially used just the SelectedItem property, but when I encountered this bug I thought using the SelectedIndex property would help to fix it.
I used ObservableCollection to maintain consistency with the other viewModels in the project, and to ensure that the options in the picker are updated when I make changes (based on my understanding you need to use ObservableCollection to make this possible).
I do plan to refactor the code in the setter for SelectedFrame into smaller functions as soon as I get things to work.
Due to this It seems that using the SelectedIndexChanged event of the Picker would be the only way to fix this. However the comment by ottermatic in this question says that events are unreliable. Hence I felt is was better to perform this logic in the setter.
If someone could comment on what I may be doing wrong in my code which is causing this issue and also comment on the pros/cons and/or whether or not I should use the eventHandler or have the logic in my setter. Thanks
I don't really see why you are using both SelectedItem and SelectedIndex, but I think what you are trying to achieve can be achieved easier.
First of all, I don't think you need ObservableCollection types at all in your example, since you are setting the directions anyway and not modifying the collection. More importantly, fiddling around with the indices is completely unnecessary, as far as I can tell. You are using strings anyway and even though String is not a value type, but a reference type, you cannot practically distinguish two String instances that have the same content, hence assinging the respective values to SelectedDirection and SelectedFrame is sufficient.
The following checks seem redundant to me
if (Directions.Contains(value))
if (EnumUtils.ToEnumFromString<FrameType>(SelectedFrame) == FrameType.Road &&
!DirectionsRoad.Contains(value))
since Directions are set to DirectionsRoad anyway if SelectedFrame has been set to "Road". Hence I'd assume that the second condition won't evaluate to true in any case. Hence the SelectedDirection can be simplified:
public string SelectedDirection
{
get => _selectedDirection;
set
{
if (_selectedDirection != value && Directions.Contains(value))
{
_selectedDirection = value;
OnPropertyChanged();
}
}
}
Within the setter of SelectedFrame there are many things going on, which I'd refactor to methods on it's own right, to improve clarity.
public string SelectedFrame
{
get => _selectedFrame;
set
{
if (_selectedFrame != value)
{
_selectedFrame = value;
UpdateAvailableDirections();
OnPropertyChanged();
}
}
}
private void UpdateAvailableDirections()
{
// store the selected direction
var previouslySelectedDirection = SelectedDirection;
Directions = GetValidDirectionsByFrameType(EnumUtils.ToEnumFromString<FrameType>(SelectedFrame));
SelectedDirection = GetSelectedDirection(previoslySelectedDirection, Directions);
}
private string[] GetValidDirectionsByFrameType(FrameType frameType)
{
return frameType == FrameType.Road ? DirectionsRoad : DirectionsAll;
}
private string GetSelectedDirection(string previouslySelectedDirection, string[] validDirections)
{
return validDirections.Contains(previouslySelectedDirection) ? previouslySelectedDirection : DefaultDirection;
}
By setting the SelectedItem instead of fiddling with the indices, the correct values shall be displayed.
Concerning your question whether this logic may be better suited in an event handler or in the setter depends on your requirements. If all you need is the index, the event SelectedIndexChanged may work out for you, but if the value is needed in several places and methods that are not called by the event handler, the presented solution may be more viable.
Edit
You were correct, it has got nothing to do with the usage of SelectedIndex and SelectedItem. The issue is a bit more subtle.
I build a quick proof-of-concept and found the following:
Assuming SelectedDirection is "Right" (and the index is set accordingly)
When Directions is set, the SelectedItem on the picker seems to be reset
SelectedDirection is set to null
this.Directions.Contains(value) evaluates to false, hence _selectedDirection is not set (this hold true for SelectedDirectionIndex, since the value -1 is filtered by if(!value < 0 || value >= this.Directions.Count))
When SelectedDirection is set afterwards, the value is still "Right", hence OnPropertyChanged is not called (since the values are the same) and the SelectedItem is not set
This way there is a mismatch between the value the Picker actually holds and the property in the viewmodel, which leads to unexpected behavior.
How to mitigate the issue?
I'd still stick with the code without the indices (unless you really need them) and use the string values.
There are other possibilities, but I'd change the setter of SelectedDirection. When you allowed the value to be set to null, PropertyChanged will be raised properly when the value is set to Right afterwards. If you really need to filter what the value is set to, you should still raise OnPropertyChanged, to inform the Picker that the value has changed (preventing a mismatch between the actual Pickers value and the viewmodel)
public string SelectedDirection
{
get => _selectedDirection;
set
{
if (_selectedDirection != value)
{
if(Directions.Contains(value))
{
_selectedDirection = value;
}
else
{
_selectedDirection = DirectionTypes.Right.ToString();
}
OnPropertyChanged();
}
}
}
Found a somewhat hacky fix for this, and it seems to be a Xamarin issue. I have made the following changes to my code"
The relevant changes are in the setter for SelectedFrame:
public string SelectedFrame
{
get { return _selectedFrame; }
set
{
if (_selectedFrame != value)
{
_selectedFrame = value;
if (EnumUtils.ToEnumFromString<FrameType>(_selectedFrame) == FrameType.Road)
{
Directions = DirectionsRoad;
if (Directions.Contains(SelectedDirection))
{
/*Relevant edits*/
var position = Directions.IndexOf(SelectedDirection);
SelectedDirection = Directions[Directions.Count - position -1];
SelectedDirection = Directions[position];
}
else
{
SelectedDirectionIndex = Directions.IndexOf(DirectionType.Right.ToString());
}
}else if (EnumUtils.ToEnumFromString<FrameType>(_selectedFrame) == FrameType.Lane)
{
Directions = DirectionsAll;
/*Relevant edits*/
var position = Directions.IndexOf(SelectedDirection);
SelectedDirection = Directions[Directions.Count - position -1];
SelectedDirection = Directions[position];
}
OnPropertyChanged();
}
}
}
}
It seems that my issue arises when I change the contents of my ObservableCollectoin but the SelectedDirection stays the same.
When I change Directions (which is an ObservableCollection) by assigning it to DirectionsAll (also an ObservableCollection), I need to make sure that the SelectedDirection changes,. The added code ensures that a change actually occurs to SelectionDirection and that fixes it. Seems somewhat hacky but it works.
Outcome:

How to avoid flickering when using an asynchronous binding

I've created an example to illustrate my problem.
ViewModel:
public class VM : INotifyPropertyChanged
{
private double _value = 1;
public double Value
{
get { return _value; }
set
{
_value = value;
OnPropertyChanged();
}
}
public VM()
{
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromTicks(1);
timer.Tick += (s, e) => { Value += 1; };
timer.Start();
}
// OnPropertyChanged stuff ...
}
}
View:
<Window.DataContext>
<namespace:VM/>
</Window.DataContext>
<Grid>
<TextBox Text="{Binding Value, IsAsync=True, FallbackValue=Test}"/>
</Grid>
When running my application the text in the textbox flickers. During the update process the FallbackValue is displayed, which makes no sense to me.
Does anyone knows the purposes or what are the benefits that during the update process the FallbackValue is displayed? Is there a way to display the old Value during an async update process?
This seems normal to me, given that you are using IsAsync=True in your binding. From the documentation:
While waiting for the value to arrive, the binding reports the FallbackValue, if one is available
When the PropertyChanged event is raised, WPF initiates the process of updating the target of the binding. Normally this would happen synchronously, with the property getter called immediately to update the value.
But you are using IsAysnc=True, so instead WPF fills in the target with the fallback value, and starts an asynchronous request to retrieve the actual property value later. Until that request has completed, the fallback value is displayed.
Does anyone knows the purposes or what are the benefits that during the update process the FallbackValue is displayed?
Per the documentation, the intent behind the IsAsync=True setting is that it's used when the property getter is, or could be, slow. Your code has told WPF that the property value has changed, so it knows the old value is no longer valid. Your code has also told (via the IsAsync in the XAML) that the property getter could take some time to provide the new value, so it defers retrieving that value until later.
In the meantime, what should WPF display? That's what the fallback value is there for.
Is there a way to display the old Value during an async update process?
If you don't want the behavior that is designed for this feature in WPF, you should just retrieve the new data asynchronously yourself, and update the property via the setter when you have it. It's not a good idea for a property getter to be slow anyway, so this would be a better design in any case.
I had the same problem but with an image source. I've removed the IsAsync on the binding and I have made my getter async:
// This field hold a copy of the thumbnail.
BitmapImage thumbnail;
// Boolean to avoid loading the image multiple times.
bool loadThumbnailInProgress;
// I'm using object as the type of the property in order to be able to return
// UnsetValue.
public object Thumbnail
{
get {
if (thumbnail != null) return thumbnail;
if (!loadThumbnailInProgress) {
// Using BeginInvoke() allow to load the image asynchronously.
dispatcher.BeginInvoke(new Action(() => {
thumbnail = LoadThumbnail();
RaisePropertyChanged(nameof(Thumbnail));
}));
loadThumbnailInProgress = true;
}
// Returning UnsetValue tells WPF to use the fallback value.
return DependencyProperty.UnsetValue;
}
}
Sometimes a binding will fail, failure is important to consider. Fallback value option presents users a message if an error occurs, rather than nothing happening. If you would like your fallbackvalue to display the previous value contained, I could think of a few ways of trying : possibly saving the value in a reference string and/or to another control, then binding to that control
But if you don't want the fallbackvalue displayed at all, you need to do a code inspection to see how your binding is failing/or is slow, and contain it in your code behind
I've found an approach to avoid flickering by just inheriting from textbox and overriding it's textproperty-metadata.
Custom TextBoxControl
public class CustomTextBox : TextBox
{
static CustomTextBox()
{
TextProperty.OverrideMetadata(typeof(CustomTextBox), new FrameworkPropertyMetadata(null, null, CoerceChanged));
}
private static object CoerceChanged(DependencyObject d, object basevalue)
{
var tb = d as TextBox;
if (basevalue == null)
{
return tb.Text;
}
return basevalue;
}
}
View
<Window.DataContext>
<namespace:VM/>
</Window.DataContext>
<Grid>
<namespace:CustomTextBox Text="{Binding Value, IsAsync=True}"/>
</Grid>
It's important to have the text-binding without a fallbackvalue. So during update process the text is set to the textproperty defalut value - so in this case to null.
The CoerceChanged handler checks whether the new value is null. If it's so he returns the old value so that during update process there is still the old value displayed.

Two-way bind a "virtual" list of strings to a column

I have a list of Strings.
Well, conceptually. They are stored somewhere else, but I want provide an object which acts like a list (and provides any necessary events on top of that), with properties that I could bind to.
I want to establish a two-way binding over this data, to display it as a modifiable column in a DataGrid. I have the following problems with that:
I can't make a two-way binding because the binding needs a path (i.e. I can't have it look like {Binding} or {Binding Path=.} in the column, must be {Binding Path=someField"} to be made modifiable if I got this right, which sounds reasonable).
I don't exactly know how the proxy collection object should look like, in terms of interfaces (would IEnumerable + INotifyCollectionChanged sufficient?)
Is there any solution which doesn't involve creating one proxy object per every String in the collection? Could you suggest an efficient design?
To keep the discussion on the rails, let's assume I want to bind to something like this:
class Source {
public String getRow(int n);
public void setRow(int n, String s);
public int getCount();
public void addRow(int position, String s);
public void removeRow(int position);
}
That's not exactly my case, but when I know how to bind to this, I think I'll be able to handle any situation like this.
I'm OK with having to provide an adapter object on top of that Source, with any necessary interfaces and events, but I don't want to have one adapter object per row of data.
While making an adapter for the Source is relatively clear, then, unfortunatelly, the core of the second problem ('not wrapping every string in a miniobject') is a clash built into the .Net and WPF..
The first thing is that the WPF does provide you with many ways of registering 'on data modified' callbacks, but provides no way of registering callbacks that would provide a value. I mean, the "set" phase is only extendable, not interceptable, and the "get" - nothing at all. WPF will simply keep and return whatever data it has once cached.
The second thing is that in .Net the string is ... immutable.
Now, if ever you provide a string directly as a pathless binding or as a datacontext to any control, you are screwed in a dead end. The problem is, that WPF actually passes only the actual value of the binding, without the information of "where it came from". The underlying control will be simply given the string instance, and will have no sane way of modifying it as the string cannot change itself. You will not be even notified about such attempt, just like with read-only properties. What's more - if you ever manage to intercept such a modification attempt, and if you produce a proper new string, the WPF will never ask you again for the new value. To update the UI, you'd have to mannually, literally, force the WPF to re-ask you by for example changing the original binding so it points elsewhere (to the new value) or set the datacontext (to the new instance). It is doable with some VisualTree scanning, as every 'changed' callback gives you the DependencyObjects (Controls!), so yo ucan scan upwards/downwards and tamper with their properties.. Remember that option - I'll refer to this in a minute.
So, everything boils down to the fact that to get a normal 2-way binding you do not have to have a Path, you "just" have to have a mutable underlying data object. If you have immutable one - then you have to use a binding to a mutable property that holds the immutable value..
Having said that, you simply have to wrap the strings some how if you want to modify them.
The other question is, how to do that. There's a plenty of ways to do it. Of course, you can simply wrap them like Joe and Davio suggested (note to Joe: INotify would be needed there also), or you can try to do some XAML tricks with attached properties and/or behaviours and/or converters to do that for you. This is completely doable, see for example my other post - I've shown there how to "inject a virtual property" that pulled the data completely from elsewhere (one binding+converter performed the wrapping on the fly, second binding extracted the values from the attached-wrapper). This way you could create a "Contents" property on the string, and that property could simply return the string itself, and it'd be completely 2-way bindable with no exceptions.
But.. it would NOT work 2-way-ish.
Somewhere at the root of your binding/behaviour/conveter chain, there will be an immutable string. Once your smart autowrapping binding chain fires with 'on modified' callback you will be notified with pair of old/new values. You will be able to remap the values to new and old strings. If you implemented everything perfectly, the WPF will simply use the new value. If you tripped somewhere, then you will have to push the new value artificially back to the UI (see the options I'd asked you to remember). So, it's ok. No wrapper, old value was visible, it was changeable, you've got new value, the UI displays new value. How about storage?
Somewhere in the meantime you've been given a old/new value pair. If you analyze them, you'll get old/new strings. But how do you update the old immutable string? Can't do. Even if autowrapping worked, even if UI worked, even if editing seemed to work, you are now standing with the real task: you onmodified callback was invoked and you have to actually update that immutable string piece.
First, you need your Source. Is it static? Phew. What a luck! So surely it is instanced. In the on-modified callback we got only a old+new string.. how to get the Source instance? Options:
scan the VisualTree and search for it in the datacontexts and use whatever was found..
add some more attached properties and binding to bind a virtual "Source" property to every string and read that property from the new value
Well doable, but smells, but no other options.
Wait, there's more: not only the old/new value and an instance of Source are needed! You also need the ROW INDEX. D'oh! how to get that from the bound data? Again, options:
scan the VisualTree and search for it (blaargh)...
add some more attached properties and bindings to bind a virtual "RowIndex" property to every (blaaergh)...
At this point of time, while I see that all of this seems implementable and actually might be working properly, I really think that wrapping each string in a small
public class LocalItem // + INotifyPropertyChanged
{
public int Index { get; }
public Source Source { get; }
public string Content
{
get { Source...}
set { Source... }
}
}
will simply be more readable, elegant and .. SHORTER to implement. And less error-prone, as more details will be explicit instead of some WPF's binding+attached magic..
I find your approach a little weird.
DataGrids are usually used to display Rows. Rows consist of data that belongs together.
You could for instance easily map a row to a certain class. This means that the columns in your datagrid represent properties in your class.
What you're trying to do is the opposite, you're trying to get a relation between the column values instead of the row values.
Wouldn't it be easier to have a collection of your class which you can then bound the column to?
For instance
class MyClass : INotifyPropertyChanged
{
// Remember to actually implement INotifyPropertyChanged
string Column;
}
If you would have an ObservableCollection of MyClass you could bind the DataGrid to this collection. Whenever the property which I called "Column" changes, you could update your special list.
You can do this by hooking up some events. With the implementation of INotifyPropertyChanged, your columns will be updated if you update the "Column"-value directly.
I have this bit of code I use to bind a list of custom object to a DataContextMenu. You can alter it to use a list of strings and bind it to what you need
class SampleCode
{
class Team
{
private string _TeamName = "";
private int _TeamProperty1 = 0;
ObservableCollection<Territory> _Territories = new ObservableCollection<Territory>();
public Team(string tName)
{
this.TeamName = tName;
}
public ObservableCollection<Territory> Territories
{
get { return _Territories; }
set { _Territories = value; }
}
public string TeamName
{
get { return _TeamName; }
set { _TeamName = value; }
}
public int TeamProperty1
{
get { return _TeamProperty1; }
set { _TeamProperty1 = value; }
}
}
class Territory
{
private string _TerritoryName = "";
Team _AssociatedTeam = null;
public Territory(string tName, Team team)
{
this.TerritoryName = tName;
this.AssociatedTeam = team;
}
public Team AssociatedTeam
{
get { return _AssociatedTeam; }
set { _AssociatedTeam = value; }
}
public string TerritoryName
{
get { return _TerritoryName; }
set { _TerritoryName = value; }
}
public void Method1()
{
//Do Some Work
}
}
class MyApplication
{
ObservableCollection<Team> _Teams = new ObservableCollection<Team>();
ContextMenu _TeritorySwitcher = new ContextMenu();
public MyApplication()
{
}
public void AddTeam()
{
_Teams.Add(new Team("1"));
_Teams.Add(new Team("2"));
_Teams.Add(new Team("3"));
_Teams.Add(new Team("4"));
foreach (Team t in _Teams)
{
t.Territories.Add(new Territory("1", t));
t.Territories.Add(new Territory("2", t));
t.Territories.Add(new Territory("3", t));
}
SetContextMenu();
}
private void SetContextMenu()
{
HierarchicalDataTemplate _hdtTerritories = new HierarchicalDataTemplate();
_hdtTerritories.DataType = typeof(Territory);
HierarchicalDataTemplate _hdtTeams = new HierarchicalDataTemplate();
_hdtTeams.DataType = typeof(Team);
FrameworkElementFactory _TeamFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TeamFactory.Name = "txtTeamInfo";
_TeamFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TeamProperty1"));
FrameworkElementFactory _TerritoryFactory = new FrameworkElementFactory(typeof(TreeViewItem));
_TerritoryFactory.Name = "txtTerritoryInfo";
_TerritoryFactory.SetBinding(TreeViewItem.HeaderProperty, new Binding("TerritoryProperty1"));
_hdtTeams.ItemsSource = new Binding("Territories");
_hdtTeams.VisualTree = _TeamFactory;
_hdtTerritories.VisualTree = _TerritoryFactory;
_hdtTeams.ItemTemplate = _hdtTerritories;
_TeritorySwitcher.ItemTemplate = _hdtTeams;
_TeritorySwitcher.ItemsSource = this._Teams;
}
}
}
Lazy solution
Derive from ObservableCollection<string> and let that collection be populated from the Source. In the derived class, register to collection change events and update the source accordingly. Bind the DataGrid column to the observable collection.
This should be pretty straightforward to write, but has a big drawback of duplicating all data in the collection.
More efficient solution
Create an adapter (as you suggested) and implement IList<string> and INotifyCollectionChanged. Let the list operations fall through directly to the source. Bind the DataGrid column to the adapter.
This approach would require some tedious boilerplate, but it's a thin layer between the WPF control and your Source.
This really depends on how you're implementing the UI. Bea Stollnitz did an excellent post of virtualizing the ItemsSource for the WPF DataGrid at http://bea.stollnitz.com/blog/?p=344 . With work I used this to edit as well as display data.
The easiest way is by placing the string in a wrapper class.
public class Wrapper
{
public string Content{get;set;}
}
Then you use the string via the wrapper class. This was the list items remain the same but the content changes.
The problem is when you do this without that then an old string is being deleted and a new one is created and the collection is confused.
Start with an ObservableCollection<string>. Then set the bindable control's ItemsSource to the ObservableCollection.

Why my control's properties won't change outside its class?

I'm new in C# but not new to coding --being doing it for almost two decades--, and have a problem with properties in a custom control I'm building, which inherits from a Panel. When I put my properties, I can see them in the Designer properties list and can even set them, but when running my little application, it seems these properties values are not used. The same if I change a property programatically: no error but my control does nothing, it is like they are not properly set. However, if I do it programatically whithin the class, they do work. My guess is that something in my properties set/get stuff is not right. Please see the following code chunk of how I'm doing it:
public class ColorStrip : Panel
{
// properties
// ------------------------------------------
// size of color clusters (boxes)
private int _clusterSize = 20;
// controls if show the buttons panel
private Boolean _showButtons;
// property setters/getters
// ------------------------------------------
// clusterSize...
public int clusterSize
{
get { return _clusterSize; }
set { _clusterSize = value; }
}
// showButtons...
public Boolean showButtons
{
get { return _showButtons; }
set { Console.Write(_showButtons); _showButtons = value; }
}
....
So in my form, for instance in the load or even in a click event somewhere, if I put colorStrip1.showButtons = false; or colorStrip1.showButtons = true; whatever (colorStrip1 would be the instance name after placing the control in the form in design mode)... console.write says always 'false'; Even if I set it in the design properties list as 'true' it will not reflect the settled value, even if I default it to true, it will never change externally. Any ideas? Non of the methods get the new and externally settled property value neither, obviously the getter/setter thing is not working. Seems to me I'm not doing right the way I set or get my properties outside the class. It works only inside it, as a charm...Any help...very appreciate!
Cheers
lithium
p.s. TO CLARIFY SOLUTION:
Setting the property in this case didn't work because I was trying to use a new set value within the constructor, which seems can't get the new values since it is, well, building the thing. If I change the property value in Design mode > Property editor or in code externally to the object, say in it's parent form's load event, it will change it but readable for all methods except the constructor, of course :)
It's likely an issue of the order of execution. Your property setter just sets a variable, but doesn't actually trigger anything on the control to update the state related to this variable (e.g. adding or showing the buttons I assume).
When you set the property befre the rest of the initialization is done, the value is being used, otherwise it isn't because during the initial go the default value is still the property value.
You need to act on the setter, here's some pseudocode to illustrate:
set {
_showButtons = value;
if (alreadyInitialized) {
UpdateButtons();
}
}
Note: make sure to first set the value, then act - otherwise you end up using the old value (just like your Console.Write() is doing).
The quoted code doesn't look problematic. Are you sure you're referencing the same instance of ColorStrip? Also, check your .Designer.cs file to ensure that the code setting the property is there.
In fact, try simplifying your code by using auto-implementing properties:
public int clusterSize { get;set;}
public Boolean showButtons {get;set;}
public ColorStrip() { ... clusterSize = 20; ... }

C# grid binding not update

I have a grid that is binded to a collection. For some reason that I do not know, now when I do some action in the grid, the grid doesn't update.
Situation : When I click a button in the grid, it increase a value that is in the same line. When I click, I can debug and see the value increment but the value doesn't change in the grid. BUT when I click the button, minimize and restore the windows, the value are updated... what do I have to do to have the value updated like it was before?
UPDATE
This is NOT SOLVED but I accepted the best answer around here.
It's not solved because it works as usuall when the data is from the database but not from the cache. Objects are serialized and threw the process the event are lost. This is why I build them back and it works for what I know because I can interact with them BUT it seem that it doesn't work for the update of the grid for an unkown reason.
In order for the binding to be bidirectional, from control to datasource and from datasource to control the datasource must implement property changing notification events, in one of the 2 possible ways:
Implement the INotifyPropertyChanged interface, and raise the event when the properties change :
public string Name
{
get
{
return this._Name;
}
set
{
if (value != this._Name)
{
this._Name= value;
NotifyPropertyChanged("Name");
}
}
}
Inplement a changed event for every property that must notify the controls when it changes. The event name must be in the form PropertyNameChanged :
public event EventHandler NameChanged;
public string Name
{
get
{
return this._Name;
}
set
{
if (value != this._Name)
{
this._Name= value;
if (NameChanged != null) NameChanged(this, EventArgs.Empty);
}
}
}
*as a note your property values are the correct ones after window maximize, because the control rereads the values from the datasource.
It sounds like you need to call DataBind in your update code.
I am using the BindingSource object between my Collection and my Grid. Usually I do not have to call anything.

Categories

Resources