i'm kind of stuck here. i'm using a dragndrop-library which serializes the dragged UIElements via XamlWriter.Save().
now i'm trying to dragndrop some instances of a class extending Grid. in the constructor i push some elements into the visual tree.
the constructor gets called when the object is deserialized and the elements get added again (and again and again depending on how often i dragndrop). sadly Children.Count tells me there are no child elements if i retrieve the value from within the constructor. if i retrieve it from outside (by myGrid.Children.Count) it gives me the higher (unwanted) amount.
is there any other function i should use to initialize the visuals? or any other way to prevent those duplicates?
thanks and cheers
took a while, but i seem to have found a solution.
now i'm able to create a base class already containing the visual elements all the subclasses need. and they are parsable via XamlWriter/Reader without duplicates. whew.
1) extend UserControl (dont Add->UserControl but Add->Class and inherit manually)
2) put the standard InitializeControl(); into the constructor
3) implement InitializeControl() and put the layouting, child adding and whatever in there
4) xamlwrite/xamlread/inherit like crazy
hope it will be helpful to someone and that i havent overseen the unforeseen..
--
edit: of course.
there will be duplicates but we cant see them.
after the XamlReader is through there are the UIElements created by my InitializeComponent() AND the ones getting xaml-parsed after that. any references in the code regard the code-created controls which are NOT show :/
sigh.
one far from perfect way around this is to put a switch into an Initialized event handler, like so:
if(HasContent)
Initialize();
else
Reinitialize();
Multiinitialize();
where the Initialize() would add the UIElements to the visual tree, the Reinitialize() just finds the right references (by control = FindName("controlName")) and the Multiinitialze() recreates the event handlers and what else gets lost on the way throug the XamlReader.
well, now you can instantiate the custom inherited UserControl in xaml but there is no easy way to set attributes. that is because setting the attributes happens before the Initialized-event and there are NullPointerExceptions awaiting. you could work around that with bindings i guess.
but there HAS to be an easier way. please let me know!
Related
I have a property called PriceChangeInPercentWeekly which has other properties like LastOpenPrice, LastClosePrice, etc.
I am changing these continuously. But how can I raise PropertyChangedEventHandler for PriceChangeInPercentWeekly when a sub-property is changed?
When PriceChangeInPercentWeekly itself is changed, I raised it using my custom SetField function. But how can I do this for sub-properties. Because sub-properties do not know the instanced class, right?
PriceChange priceChangeInPercentWeekly;
public PriceChange PriceChangeInPercentWeekly
{
get => this.priceChangeInPercentWeekly;
set => SetField ( ref this.priceChangeInPercentWeekly, value,
"PriceChangeInPercentWeekly"
);
}
Based on the comments, it sounds like you have a issue with properly binding in a collection case. When dealing with collections in MVVM, there are actually 3 seperate kinds of bindings/Change Notifications you need. If any one is missing, you end up getting issues with non-updates pretty quickly:
Whatever class is holding LastOpenPrice, LastClosePrice, PriceChange and the like - it needs change notificaiton on each and every property. Whatever template you use to display that class, needs to properly bind to each of those properties.
You need change notificaiton if anything is added or removed from the Collection. That is the only change notification ObservableCollection<ClassThatHoldsThosePrices> takes care off.
Whatever property exposes the ObservableList<ClassThatHoldsThosePrices> or its Collection View needs change notificaiton as well. ObservableCollections do not support bulk additions. With a big changes you usually have to build a new list in the background, then replace the whole collection.
At this point it usually becomes a question of properly setting up (or automatically having set up) the 3 kinds of bindings. The proper MVVM pattern is not a easy thing to learn and it can not tell if you did it properly, so it is hard to say where the issues could be. About 8 years ago I wrote a short introducing which I think should still be okay-ish for learning the basics: https://social.msdn.microsoft.com/Forums/vstudio/en-US/b1a8bf14-4acd-4d77-9df8-bdb95b02dbe2/lets-talk-about-mvvm?forum=wpf
I've recently needed to synchronize values from a higher up view model (bound to a custom tab content control) to the base one (bound to the host view) for the CanExecute delegate in the host view to use. The base model has the instance of the top one, among others.
The only thing that I can think of for it to know when a value changes higher up is to subscribe to the PropertyChanged event. But, that seems excessive considering how many times that event would get fired for all of the other properties. It also doesn't feel right for MVVM (but I may be wrong).
Right now, I'm setting it all in a central method in the manager class where the magic happens to make sure the values match up:
If setDealable Then multilegViewModel.IsDealable = isDealable
multilegViewModel.IsIndicative = (Not isDealable)
' [...]
tktViewModel.IsCommandOtherEnabled = (Not isDealable)
tktViewModel.IsCommandBuyEnabled = multilegViewModel.IsBuyButtonEnabled
tktViewModel.IsCommandSellEnabled = multilegViewModel.IsSellButtonEnabled
tktViewModel.IsDealable = isDealable
tktViewModel.IsIndicative = (Not isDealable)
' [...]
But, smaller sets of the "multilegViewModel" properties are being set elsewhere, so I have to find them all and add the copy over. The risk is having another developer leave out such a pairing somewhere.
So, does anyone have ideas other than one view model subscribing to the other to ensure that the values always get set?
Note: The dual language tags are on purpose. It's a mixed language solution, such as the manager being VB.NET, but the models are C#, so I accept suggestions in either one.
UPDATE: I've changed my approach and greatly simplified it, so my initial reason for needing this is no longer valid. But, I may have an edge case or two that could still benefit from this.
Is it possible to just "link" two properties of different ViewModels in a way such that when one property changes, the other one changes too. So in essence, I want two properties in different ViewModels behave as if they were one.
It would be nice if I could just do something like the following in my ViewModels:
WhenPropertyChanges(() => SettingX).CopyValueTo(() => ModelView2.SettingX);
Example: On my settings page, when I change a setting, I want the new value to be available in the ViewModel of another page.
I know I can achieve that with the third Layer (Model), but it feels a bit clunky and the way I solved it for now doesn't feel right (Firing events).
I've implemented following MVVM-Pattern from this page: http://www.wintellect.com/devcenter/krome/linking-property-change-notifications-in-xamarin-forms-or-wpfsilverlight. As far as I understood, the author says his code makes it possible, but unfortunately he doesn't show how.
I also found this article (http://blog.alectucker.com/post/2014/07/26/using-messageingcenter-in-xamarin-forms-for-viewmodel-to-viewmodel-navigation.aspx), but I don't necessarily want to navigate to the other View. Still, maybe I can use this MessagingCenter somehow to achieve my goal?
This is addressed in the final paragraph of my article that you reference. I use a simple extension method to do it. An example of this can be seen in the source code example from the article, in this file: https://github.com/Wintellect/XamarinSamples/blob/master/PropertyDependencyDemo%2FPropertyDependencyDemo%2FMvvm%2FObservableExtensions.cs
For your specific example, it would look something like this:
// using PropertyDependencyDemo.Mvvm;
// ... use the namespace above that contains the ObservableExtensions class
ModelView1
.WhenPropertyChanges((a) => a.SettingX)
.AlsoInvokeAction(() => ModelView2.SettingX = ModelView1.SettingX);
All this does is hook into the PropertyChanged event of the source viewmodel for you in a name-safe way.
One word of caution though: you need to be careful to not create a situation where you inadvertently prevent an object from being garbage collected. The reason this can happen is that in this example, ViewModel1 will now have a PropertyChanged handler that references a PropertyDependency object that in turn references both ModelView1 and ModelView2 due to the captured references in the Action lambda expression.
IF you know for sure that this won't be a problem (perhaps both go out of scope together), then there is nothing to worry about. But if you find yourself facing a situation where you need to prevent ModelView1 from keeping ModelView2 pinned, then you can do so using a WeakReference. Again, this is unlikely to be a concern, but if you find it leaking memory then you can change the above to this:
// assuming "TModelView" is the class name of the viewmodels
var wr = new WeakReference<TModelView>(ModelView2);
ModelView1
.WhenPropertyChanges((a) => a.SettingX)
.AlsoInvokeAction(() => {
TModelView mv;
if (wr.TryGetTarget(out mv))
mv.SettingX = ModelView1.SettingX;
});
I think if the navigation is lineare ( you only need the information of page 1 in page 2 ) you can use the MessagingCenter to do what you want.
If the user is able to change the data in the page 2 ( and be updated in the page 1 ) the messagingCenter is a little to tricky to use. I think you can use the first link you provide. Just use an abstract class with all shared data in your ViewModels.
If you want more help put an more completed code example.
So, I've just discovered the wonderful DebuggerDisplay attribute, and have started using it in my code. I've noticed something really quirky about it, though, or perhaps the issue is actually with KeyedCollection.
Without a DebuggerDisplay attribute, I get "Count = x" as the primary display for a KeyedCollection, and that expands to show the elements of the collection. Adding the attribute, however, I get my custom text, as expected, but it now expands to show the various values and properties that I've added to the object itself. That's great, and makes far more sense to me than the collection-->Raw View style that shows up by default. Now, though, the Results View isn't populated by default, it instead has the customary "Expanding the Results View will enumerate the IEnumerable".
How do I get rid of that behaviour? I know the results are safe to enumerate, and would like them to be enumerated automatically. For bonus points, can I have it auto-expand the selection as well?
As I said in my comments, the closest you can be to what you want is by using the DebuggerTypeProxyAttribute. Unfortunately it's a sealed type so you might not be able to make it easy to reuse.
Long story short, the .Net ConfigurationManager.GetSection method skips duplicates, and I'm looking for a best practice for handling this. I have a config file that includes something like this:
<views>
<view name="BusinessUnitsView" Desc="desc1"/>
<view name="BusinessUnitsView" Desc="desc2"/>
</views>
I have a graph of objects the config loads onto where collections derive from our derived version of ConfigurationElementCollection. The views collection in the graph only has one item in it after loading the above--my understanding is that this is simply the way the ConfigurationManager handled duplicates. The last item with the same key wins.
I could throw an exception on a duplicate being found by using BaseAdd(element, true). However, if possible I'd like to get the object completely loaded WITH duplicates, as the config gets read in a service layer, and I'd like to detect and deal with the problem on the consuming side of the service.
Do I have any options for modifying the way adds to the collection work?
You will need to create your own ConfigurationSection. See here or here (the second link's method has been deprecated, but it may still shed some light)
This allows you to represent internal configuration variables as collections, and set properties like DefaultValue and IsRequired.
Maybe you only want to iterate over a list but the main idea of the config is that you can do
var view = SomeConfigClass["BusinessUnitsView"];
That only allows one answer.