Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[WPF] NotifyPropertyChanged voor een readonly property

Pagina: 1
Acties:

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
Ik heb een applicatie in WPF die gebruik maakt van een service reference. De service reference genereert vanzelf voor mij een class Setup die ik nodig heb in de communicatie met de webservice. Deze class heeft (onder andere) een property UserId (integer) die het ID van een user aangeeft. Voor de rest heeft deze class geen informatie over de user (want dat zou dubbelop zijn).

De informatie over alle users komt via een aparte webmethod binnen die simpelweg een lijstje users en hun details ophaalt. Dit lijstje users sla ik op (in Service.Users) zodat ik er later naar kan refereren.


Om nu vanuit de class Setup de naam van de User te krijgen heb ik een nieuwe partial Setup class aangemaakt waarin ik een read-only property Username toevoeg. Deze property kijkt of een username backing field leeg is, en zo ja haalt de username eventjes op uit de Service.Users lijste:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Mijn eigen partial class:
public partial class Setup
{
    private string _username;
    public string Username
    {
        get
        {
            if (_username == null) _username = Service.Users.FromId(this.UserId).Username;
            return _username;
        }
    }
}


// Automatisch gegenereert door webservice reference:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="Setup", Namespace="...")]
[System.SerializableAttribute()]
public partial class Setup : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
{
    [System.Runtime.Serialization.DataMemberAttribute()]
    public int UserId {
        get {
            return this.UserIdField;
        }
        set {
            if ((this.UserIdField.Equals(value) != true)) {
                this.UserIdField = value;
                this.RaisePropertyChanged("UserId");
            }
        }
    }
}


Waar het dus om gaat is dat ik in een partial class een property Username toevoeg, welke de username ophaalt (vanuit het UserId wat bekend is).


Mijn probleem is nu dat de UserId property kan veranderen. De verandering van UserId wordt netjes aan mijn views doorgegeven omdat die property RaisePropertyChanged aanroept. Maar de Username wordt niet opnieuw opgehaald uiteraard, omdat er niet doorgegeven wordt dat die ook moet veranderen. Resultaat is dat de Username niet veranderd (in mijn views) als ik de user verander.

Hoe zorg ik er nu voor dat RaisePropertyChanged ook voor de Username property aangeroepen wordt? Ik kan niet in de UserId property gaan zitten schrijven want die is autogenerated.

De enige optie die ik zie is de Setup class naar zijn eigen PropertyChanged event te laten luisteren, en als UserId veranderd dan nog even RaisePropertyChanged oproepen voor Username. Maar dat lijkt heel erg omslachtig, en ook een beetje vreemd. Er moet toch wel een mooiere manier zijn om dit op te lossen?

Mijn iRacing profiel


  • frG
  • Registratie: Augustus 2004
  • Laatst online: 20-11 20:03

frG

In je RaisePropertyChanged handler kan je toch handmatig het propertyChanged event af vuren voor Username indien van toepassing?

  • BoringDay
  • Registratie: Maart 2009
  • Laatst online: 13-05 21:49
Kan je getter niet de User data meteen opvragen?
Lijkt me beetje omslachtig op deze manier en dan heb je tenminste altijd de info.

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
frG schreef op donderdag 31 juli 2014 @ 18:06:
In je RaisePropertyChanged handler kan je toch handmatig het propertyChanged event af vuren voor Username indien van toepassing?
RaisePropertyChanged zit in de autogenerated code en kan ik dus niet aanpassen. Of snap ik je verkeerd?
BoringDay schreef op donderdag 31 juli 2014 @ 19:02:
Kan je getter niet de User data meteen opvragen?
Lijkt me beetje omslachtig op deze manier en dan heb je tenminste altijd de info.
Welke getter? De enige getter die ik zelf schrijf (en dus kan aanpassen) is die van Username, en daar haal ik de data toch op..? Het probleem is dat UserId kan veranderen, waarna Username ook zou veranderen als ik die getter weer aanroep, maar die getter wordt niet aangeroepen omdat mijn views niet doorkrijgen dat Username veranderd is.


Ik kan inmiddels hier en daar wat vinden (zoekterm 'calculated property' helpt al), zoals dit:
http://www.codeproject.co...operty-dependency-problem

Maar voor mij gaat dit niet werken omdat ik dan in de autogenerated code moet gaan zitten prutsen.

[ Voor 13% gewijzigd door NickThissen op 31-07-2014 20:27 ]

Mijn iRacing profiel


  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
Ik zie nu dat het zo natuurlijk nooit gaat werken zelfs als de RaisePropertyChanged door komt. _username is daarna niet meer null en wordt dus niet opnieuw opgehaald... Ik zal dus de _username op null moeten zetten als UserId veranderd. Dit gaat een beetje uit de hand lopen (Username is niet de enige 'calculated' property op deze manier). Hoe doe ik dit slimmer?

Mijn iRacing profiel


  • frG
  • Registratie: Augustus 2004
  • Laatst online: 20-11 20:03

frG

NickThissen schreef op donderdag 31 juli 2014 @ 20:46:
Ik zie nu dat het zo natuurlijk nooit gaat werken zelfs als de RaisePropertyChanged door komt. _username is daarna niet meer null en wordt dus niet opnieuw opgehaald... Ik zal dus de _username op null moeten zetten als UserId veranderd. Dit gaat een beetje uit de hand lopen (Username is niet de enige 'calculated' property op deze manier). Hoe doe ik dit slimmer?
Lijkt me toch de beste optie om gewoon een apart viewmodel te maken, welke gebind wordt aan je xaml.
In dit viewmodel neem je een private property op die het automatisch gegenereerde object vast houdt, deze update je bij wijzigingen, en bij het opslaan stuur je die weer terug naar de web service?

Het is verder toch ook prima mogelijk om die automatisch gegenereerde class niet de IPropertyChanged interface te implementeren? Deze bouw je dan zelf in op je viewmodel.

  • Koen_R
  • Registratie: Juni 1999
  • Laatst online: 07:57
Het probleem is dat je een property misbruikt als een functie. Je wilt namelijk dat de waarde zichzelf veranderd en niet pas veranderd als hij wordt aangeroepen.

De nette methode is om een ViewModel laag rondom beide properties te bouwen. Daarin kan je een functie bouwen die de userName oproept op het moment dat de userId wijzigt en dan netjes de property wijzigt.

Het controleren op de wijziging van userId in het ViewModel zou kunnen met een PropertyDescriptor maar let wel op dat dat strong reference aanmaakt en dus voor een geheugen lek kan zorgen. Edit: zou best kunnen dat dat alleen op echte Dependency Proporties kan trouwens.

Hoe werkt die webservice eigenlijk? Hoe pushed die de nieuwe UserID's?

[ Voor 12% gewijzigd door Koen_R op 31-07-2014 20:57 . Reden: aanvulling ]


  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
Bedankt, ik denk dat er inderdaad een laag bovenop moet. Ik zal eens kijken hoe ik het kan implementeren.

Mijn iRacing profiel


  • Guldan
  • Registratie: Juli 2002
  • Laatst online: 20-11 21:10

Guldan

Thee-Nerd

Wat je zou kunnen doen is het volgende:

1.Verplaats je user class naar een aparte assembly (Datacontracts/Domain whatever)
2.Geef de user class die in je backend zit INotifyPropertyChanged
3.Voeg een reference toe van de nieuwe assembly aan je WPF project
3.Stel re-use types in bij je webservice reference en selecteer daar de zojuist toegevoegde assembly

Dan heb je INotifyPropertyChanged in je user classes. Maar ik weet niet of je daar mee weg kan komen :).

Opmerking: Mijn ervaring met Silverlight is wel goed hiermee. Echter moest je waneer je de re-use types vergeten was bij een nieuwe service wel ff de reference weggooien en opnieuw beginnen want als je het dan aan zette dan negeerde hij het gewoon.

[ Voor 21% gewijzigd door Guldan op 01-08-2014 10:04 ]

You know, I used to think it was awful that life was so unfair. Then I thought, wouldn't it be much worse if life were fair, and all the terrible things that happen to us come because we actually deserve them?

Pagina: 1