Springen naar inhoud


WPF

Aanpassing Observablecollection Niet Zichtbaar In Ui

MVVM c#

  • Log in a.u.b. om te beantwoorden
Er zijn 20 reacties in dit onderwerp

#1 Hypenate

Hypenate

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1228 berichten
    Laatst bezocht 15 mei 2019 21:27
Inzender

Geplaatst op 11 augustus 2014 - 13:37

Sub voor de aanpassingen:
Code:
		public void SaveChanges()
		{
			if (this.UserRole.UserRoleID == 0)
			{
				//Insert
				csSecurityV3Entities_PersonData ef = new csSecurityV3Entities_PersonData();
				ef.tblUserRoles.Add(new tblUserRole() { userRole = this.UserRole.UserRole1, userRoleID = this.UserRole.UserRoleID });
				ef.SaveChanges();
			}
			else
			{
				//Update
				csSecurityV3Entities_PersonData ef = new csSecurityV3Entities_PersonData();
				var Role = ef.tblUserRoles.Where(c => c.userRoleID == this.UserRole.UserRoleID).FirstOrDefault();
				Role.userRole = this.UserRole.UserRole1;
				ef.SaveChanges();
			}

			Load();
		}

En hier mijn Load() sub, waar ik heel leuk zie dat er effectief een item is bijgekomen
Code:
		public void Load()
		{
			UserRoles = new ObservableCollection<Models.UserRole>();

			using (var items = new csSecurityV3Entities_PersonData())
			{
				var userRoles = from r in items.tblUserRoles
								select r;

				foreach (var item in userRoles)
				{
					UserRoles.Add(new Models.UserRole() { UserRole1 = item.userRole, UserRoleID = item.userRoleID });
				}
			}

			UserRole = new Models.UserRole();

			UpdateCommand = new Commands.Update.UserRoleUpdateCommand(this);
			AddCommand = new Commands.Add.UserRoleAddCommand(this);
		}

En toch wordt de UI niet geupdate desondanks dat de INotifyPropertyChanged word aangeroepen...
Iemand een idee?

#2 Dirk Andries

Dirk Andries

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1189 berichten
    Laatst bezocht
  • LocatieGent

Geplaatst op 11 augustus 2014 - 13:56

Bericht bekijkenHypenate, op 11 augustus 2014 - 13:37, zei:

En toch wordt de UI niet geupdate desondanks dat de INotifyPropertyChanged word aangeroepen...
Ik kan al niet opmaken uit bovenstaande code waarom je überhaupt verwacht dat de UI zou geüpdatet worden?

#3 Hypenate

Hypenate

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1228 berichten
    Laatst bezocht 15 mei 2019 21:27
Inzender

Geplaatst op 11 augustus 2014 - 14:53

UserRoles is een property, deze roept de Inotifypropertychanged op, ik heb ook al eens expliciet deze sub aangeroepen.

Als je aanpassingen doet geeft hij deze wel meteen weer, dus het lijkt wel te werken. Enkel als ik dan alles ophaal van de database komt er niets bij in de UI.

#4 Dirk Andries

Dirk Andries

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1189 berichten
    Laatst bezocht
  • LocatieGent

Geplaatst op 11 augustus 2014 - 15:06

Bericht bekijkenHypenate, op 11 augustus 2014 - 14:53, zei:

UserRoles is een property, deze roept de Inotifypropertychanged op
Code en/of bindings?
Zonder is het gokken.

Blinde gok: als INotifyPropertyChanged wordt aangeroepen bij het wijzigen van de waarde van UserRoles is het logisch dat je met bovenstaande code geen update krijgt.
De onderliggende lijst is leeg als je hem toekent aan de property.

#5 Hypenate

Hypenate

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1228 berichten
    Laatst bezocht 15 mei 2019 21:27
Inzender

Geplaatst op 11 augustus 2014 - 15:48

De binding gebeurd via XAML, maar daar is niets mis mee.
Ook wordt de insert correct gedaan, en dit nieuwe item komt ook in de lijst UserRoles te staan maar dan stopt het verhaal... Hij geeft dit niet weer in de UI.

Je blinde gok is voor mij chinees, hoe bedoel je?

#6 Dirk Andries

Dirk Andries

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1189 berichten
    Laatst bezocht
  • LocatieGent

Geplaatst op 12 augustus 2014 - 10:05

Bericht bekijkenHypenate, op 11 augustus 2014 - 15:48, zei:

De binding gebeurd via XAML, maar daar is niets mis mee.
Ik geloof je wel, maar toon het?
Hoe kunnen we ons een beeld vormen van je opzet?

Bericht bekijkenHypenate, op 11 augustus 2014 - 15:48, zei:

Je blinde gok is voor mij chinees, hoe bedoel je?
Nou, als je de code en bindings niet toont moeten we gokken naar wat er mis loopt.
Dat is zo'n gok. Die misschien nergens op slaat.


Zie meegeleverd voorbeeld.
Noteer dat de logica in het voorbeeld niet echt kosjer is (El Jos) en waarschijnlijk niet helemaal spoort met jouw opzet.

Bijgevoegde Bestanden



#7 Dirk Andries

Dirk Andries

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1189 berichten
    Laatst bezocht
  • LocatieGent

Geplaatst op 12 augustus 2014 - 10:07

Bericht bekijkenDirk Andries, op 12 augustus 2014 - 10:05, zei:

Zie meegeleverd voorbeeld.

Oeps!
Heb het in Visual Basic (VS 2013) gemaakt (en jij doet C# ?).
In C# is het gewoon hetzelfde.

#8 Hypenate

Hypenate

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1228 berichten
    Laatst bezocht 15 mei 2019 21:27
Inzender

Geplaatst op 15 augustus 2014 - 07:53

Ja, ben overstapt naar C#, leek mij meer opportuun
Ik smijt hier eventjes mijn MVVM, sorry voor de lange post!

Model:
Code:
namespace GlobalItems.Models
{
//Used for the different roles like: Admin/Normal/Co/Lg
public class UserRole : INotifyPropertyChanged, IDataErrorInfo
{
	 private int userRoleID;
	 private string userRole;

	 public int UserRoleID
	 {
		 get { return userRoleID; }
		 set
		 {
			 userRoleID = value;
			 onpropertychanged("UserRoleID");
		 }
	 }
	 public string UserRole1
	 {
		 get { return userRole; }
		 set
		 {
			 userRole = ProperName.ProperNaming(value, ProperName.Type.Name, false);
			 onpropertychanged("UserRole1");
		 }
	 }
			
	 public UserRole() { }

	 public UserRole(int userRoleID, string userRole)
	 {
		 this.UserRoleID = userRoleID;
		 this.UserRole1 = userRole;
	 }

	 #region INotifyPropertyChanged Members

	 public event PropertyChangedEventHandler PropertyChanged;

	 private void onpropertychanged(string propertyName)
	 {
		 PropertyChangedEventHandler handler = PropertyChanged;

		 if (handler != null)
			 handler(this, new PropertyChangedEventArgs(propertyName));
	 }
	 #endregion

	 #region Validation

	 public string Error
	 {
		 get;
		 private set;
	 }

	 public string this[string columnName]
	 {
		 get
		 {
			 if (columnName == "UserRole1")
				 if (String.IsNullOrEmpty(this.UserRole1))
				 {
					 Error = "Field cannot be blank";
				 }
				 else
					 Error = null;

			 return Error;
		 }
	 }

	 #endregion

}
}

ViewModel
Code:
using GlobalItems.Commands;
using DAL;

namespace GlobalItems.ViewModels
{
	public class UserRoleViewModel
	{
		private ObservableCollection<Models.UserRole> userRoles;
		private Models.UserRole userRole;

		public ObservableCollection<Models.UserRole> UserRoles
		{
			get { return userRoles; }
			set
			{
				userRoles = value;
				RaisePropertyChanged("UserRoles");
			}
		}
		public Models.UserRole UserRole
		{
			get { return userRole; }
			set
			{
				userRole = value;
				RaisePropertyChanged("UserRole");
			}
		}

		public void Load()
		{
			UserRoles = new ObservableCollection<Models.UserRole>();

			using (var items = new csSecurityV3Entities_PersonData())
			{
				var userRoles = from r in items.tblUserRoles
								select r;

				foreach (var item in userRoles)
				{
					UserRoles.Add(new Models.UserRole() { UserRole1 = item.userRole, UserRoleID = item.userRoleID });
				}
			}

			UserRole = new Models.UserRole();

			UpdateCommand = new Commands.Update.UserRoleUpdateCommand(this);
			AddCommand = new Commands.Add.UserRoleAddCommand(this);
		}

		public UserRoleViewModel()
		{
			Load();
		}

		#region INotifyPropertyChanged

		public event PropertyChangedEventHandler PropertyChanged;

		private void RaisePropertyChanged(string propertyName)
		{
			PropertyChangedEventHandler handler = PropertyChanged;
			if (handler != null)
			{
				handler(this, new PropertyChangedEventArgs(propertyName));
			}
		}

		#endregion

		#region Commands
		private ICommand updateCommand;
		private ICommand addCommand;

		public ICommand AddCommand
		{
			get { return addCommand; }
			set { addCommand = value; }
		}
		
		public ICommand UpdateCommand
		{
			get { return updateCommand; }
			set { updateCommand = value; }
		}

		public void SaveChanges()
		{
			if (this.UserRole.UserRoleID == 0)
			{
				//Insert
				csSecurityV3Entities_PersonData ef = new csSecurityV3Entities_PersonData();
				ef.tblUserRoles.Add(new tblUserRole() { userRole = this.UserRole.UserRole1, userRoleID = this.UserRole.UserRoleID });
				ef.SaveChanges();
			}
			else
			{
				//Update
				csSecurityV3Entities_PersonData ef = new csSecurityV3Entities_PersonData();
				var Role = ef.tblUserRoles.Where(c => c.userRoleID == this.UserRole.UserRoleID).FirstOrDefault();
				Role.userRole = this.UserRole.UserRole1;
				ef.SaveChanges();
			}

			Load();
		}
		#endregion
	}
}

View (XAML)
Overzicht
XAML Code:
<Window x:Class="GlobalItems.Views.UserRole.UserRoleView"
	 xmlns="http://schemas.micro...l/presentation"
	 xmlns:x="http://schemas.micro...infx/2006/xaml"
	 xmlns:local="clr-namespace:GlobalItems.ViewModels"
	 Title="UserRoleView" Height="300" MinHeight="300" MinWidth="320" Width="320" Activated="Window_Activated">
<window.Resources>
	 <Style TargetType="TextBox" BasedOn="{StaticResource BBtxt}" />
	 <local:UserRoleViewModel x:Key="ViewModel"/>
</window.Resources>
<window.DataContext>
	 <local:UserRoleViewModel/>
</window.DataContext>

<DockPanel LastChildFill="True">
	 <Menu IsMainMenu="True" DockPanel.Dock="Top">
		 <MenuItem Header="File">
			 <MenuItem Command="{Binding AddCommand}" Header="Add"/>
		 </MenuItem>
	 </Menu>
	 <Grid DataContext="{StaticResource ViewModel}">
		 <Grid.RowDefinitions>
			 <RowDefinition Height="50"/>
			 <RowDefinition Height="*"/>
			 <RowDefinition Height="50"/>
		 </Grid.RowDefinitions>
		 <StackPanel VerticalAlignment="Top" Orientation="Horizontal" Margin="10,10,0,0">
			 <Label Content="Select:" Width="75"/>
			 <ComboBox x:Name="cboSelect" Width="188" ItemsSource="{Binding UserRoles, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True, Mode=TwoWay}"
						 DisplayMemberPath="UserRole1" SelectedValuePath="UserRoleID" SelectedItem="{Binding UserRole, Mode=TwoWay}" SelectedIndex="0"/>
		 </StackPanel>

		 <StackPanel VerticalAlignment="Top" Orientation="Horizontal" Margin="10,10,0,0" Grid.Row="1">
			 <Label Content="ID:" Width="75"/>
			 <TextBox Width="188" Text="{Binding UserRole.UserRoleID, Mode=OneWay}" IsReadOnly="True"/>
		 </StackPanel>
		 <StackPanel VerticalAlignment="Top" Orientation="Horizontal" Margin="10,41,0,0" Grid.Row="1">
			 <Label Content="Role:" Width="75"/>
			 <TextBox Width="188" Text="{Binding UserRole.UserRole1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
		 </StackPanel>

		 <Button Command="{Binding UpdateCommand}" Content="Save" HorizontalAlignment="Left" Margin="10,0,0,10" Grid.Row="2" VerticalAlignment="Bottom" Width="75"/>
	 </Grid>
</DockPanel>
</Window>

Toevoegen
XAML Code:
<Window x:Class="GlobalItems.Views.UserRole.UserRoleAdd"
	 xmlns="http://schemas.micro...l/presentation"
	 xmlns:x="http://schemas.micro...infx/2006/xaml"
	 xmlns:local="clr-namespace:GlobalItems.ViewModels"
	 Title="UserRoleView" Height="300" MinHeight="300" MinWidth="320" Width="320">
<window.Resources>
	 <Style TargetType="TextBox" BasedOn="{StaticResource BBtxt}" />
	 <local:UserRoleViewModel x:Key="ViewModel"/>
</window.Resources>

<DockPanel LastChildFill="True">
	 <Menu IsMainMenu="True" DockPanel.Dock="Top">
		 <MenuItem Header="File"/>
	 </Menu>
	 <Grid DataContext="{StaticResource ViewModel}">
		 <Grid.RowDefinitions>
			 <RowDefinition Height="201"/>
			 <RowDefinition Height="50"/>
		 </Grid.RowDefinitions>

		 <StackPanel VerticalAlignment="Top" Orientation="Horizontal" Margin="10,10,0,0">
			 <Label Content="ID:" Width="75"/>
			 <TextBox Width="188" Text="{Binding UserRole.UserRoleID, Mode=OneWay}" IsReadOnly="True"/>
		 </StackPanel>
		 <StackPanel VerticalAlignment="Top" Orientation="Horizontal" Margin="10,41,0,0">
			 <Label Content="Role:" Width="75"/>
			 <TextBox Width="188" Text="{Binding UserRole.UserRole1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
		 </StackPanel>

		 <Button Command="{Binding UpdateCommand}" Content="Save" HorizontalAlignment="Left" Margin="10,0,0,10" Grid.Row="1" VerticalAlignment="Bottom" Width="75" Click="Button_Click"/>
	 </Grid>
</DockPanel>
</Window>

Commands
Update
Code:
namespace GlobalItems.Commands.Update
{
class UserRoleUpdateCommand : ICommand
{
	 private ViewModels.UserRoleViewModel viewModel;
	 public event EventHandler CanExecuteChanged
	 {
		 add { CommandManager.RequerySuggested += value; }
		 remove { CommandManager.RequerySuggested -= value; }
	 }

	 public UserRoleUpdateCommand(ViewModels.UserRoleViewModel userRoleViewModel)
	 {
		 this.viewModel = userRoleViewModel;
	 }

	 public bool CanExecute(object parameter)
	 {
		 if (viewModel.UserRole == null)
			 return false;
		 else
			 return String.IsNullOrEmpty(viewModel.UserRole.Error);
	 }

	 public void Execute(object parameter)
	 {
		 viewModel.SaveChanges();
	 }

}
}


Add
Code:
namespace GlobalItems.Commands.Add
{
class UserRoleAddCommand : ICommand
{
	 private ViewModels.UserRoleViewModel viewModel;
	 public event EventHandler CanExecuteChanged
	 {
		 add { CommandManager.RequerySuggested += value; }
		 remove { CommandManager.RequerySuggested -= value; }
	 }

	 public UserRoleAddCommand(ViewModels.UserRoleViewModel userRoleViewModel)
	 {
		 this.viewModel = userRoleViewModel;
	 }

	 public bool CanExecute(object parameter)
	 {
		 if (viewModel.UserRole == null)
			 return false;
		 else
			 return String.IsNullOrEmpty(viewModel.UserRole.Error);
	 }

	 public void Execute(object parameter)
	 {
		 Views.UserRole.UserRoleAdd frm = new Views.UserRole.UserRoleAdd();
		 frm.ShowDialog();

		 viewModel.Load();
	 }
}
}


#9 Prior

Prior

    Rookie Developer

  • Leden
  • Pip
  • 34 berichten
    Laatst bezocht 26 sep 2017 19:33

Geplaatst op 15 augustus 2014 - 10:18

Bericht bekijkenHypenate, op 15 augustus 2014 - 07:53, zei:

Model:
public class UserRole : INotifyPropertyChanged, IDataErrorInfo
{  
#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

private void onpropertychanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;

if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}[/code]

Ik begrijp niet echt goed waarom je na het aanmaken van een event PropertyChangedEventHandler PropertyChanged, je opnieuw een variabele maakt voor die event en dan PropertyChanged eraan toekent ... :S

Je kan toch ook zeggen:

Code:
protected void onpropertychanged(string propertyName)
		{
			if (PropertyChanged != null)
			{
				PropertyChanged(this, new PropertyChangedEventArgs(name));
			}
		}



#10 Hypenate

Hypenate

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1228 berichten
    Laatst bezocht 15 mei 2019 21:27
Inzender

Geplaatst op 15 augustus 2014 - 10:28

Je kan toch ook zeggen:

Citeren

Code:
protected void onpropertychanged(string propertyName)
	 {
		 if (PropertyChanged != null)
		 {
			 PropertyChanged(this, new PropertyChangedEventArgs(name));
		 }
	 }


Ja, dat scheelt hem een stukje in de code ja...
Geen idee waarom hij dit doet, ik heb deze tutorials gevolgd:
https://www.youtube....D29FB131ADF509F

#11 josk79

josk79

    Master Developer

  • Leden
  • PipPipPipPipPip
  • 614 berichten
    Laatst bezocht 07 apr 2020 23:55

Geplaatst op 15 augustus 2014 - 15:06

In onderstaande code kan in zeeeeeeeer uitzonderlijke gevallen een 'race conditie' ontstaan; als vanuit een andere thread de PropertyChanged handler wordt verwijderd, nadat de != null controle is uitgevoerd, maar voordat PropertyChanged(...) wordt aangeroepen, krijg je een null reference exception. Dat is de reden dat je in vele voorbeelden die extra variabele gebruikt ziet worden.


Code:
protected void onpropertychanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}



#12 Hypenate

Hypenate

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1228 berichten
    Laatst bezocht 15 mei 2019 21:27
Inzender

Geplaatst op 17 augustus 2014 - 08:53

Bericht bekijkenDirk Andries, op 12 augustus 2014 - 10:05, zei:

Zie meegeleverd voorbeeld.
Noteer dat de logica in het voorbeeld niet echt kosjer is (El Jos) en waarschijnlijk niet helemaal spoort met jouw opzet.

Ik zie vooralsnog geen verschil tussen jouw & mijn programma...behalve dat het mijne niet werkt :)

-edit-
Is het misschien omdat ik het object aanmaak in een 2e formulier/view?!

#13 Prior

Prior

    Rookie Developer

  • Leden
  • Pip
  • 34 berichten
    Laatst bezocht 26 sep 2017 19:33

Geplaatst op 17 augustus 2014 - 14:18

<window.Resources> en <window.DataContext> moet met hoofdletter W ipv kleine letter.

Is het mogelijk om de volledige solution te posten, ik wil er wel eens naar kijken, alhoewel dat ik geen mvvm heb gezien.

Overigens verwijst de xaml code naar BBtxt (style based on welke control?), wat ben je hier mee van plan?

#14 Dirk Andries

Dirk Andries

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1189 berichten
    Laatst bezocht
  • LocatieGent

Geplaatst op 17 augustus 2014 - 14:42

Bericht bekijkenHypenate, op 17 augustus 2014 - 08:53, zei:

Ik zie vooralsnog geen verschil tussen jouw & mijn programma.
Is het misschien omdat ik het object aanmaak in een 2e formulier/view?!
Neen, dat is (op zich) niet de reden.
Je hebt 1 viewmodel voor twee views, wat ik niet snel zou doen.
Je Model.UserRole is eigenlijk een half ViewModel:
    - wat doet die columname daar als parameter?
    - waarom is dat een IPropertyChanged implementor?
    - waar komt het idee van die validatie vandaan?
Was het niet jouw bedoeling een UserViewModel te maken?

Kan je niet het hele project (eventueel minus databank en DAL) posten.
Ik heb geprobeerd (zoals Prior hier, vermoed ik) je project terug samen te stellen uit je gegeven code, echter bots ik tegen wat fouten op waarvan ik vermoed dat ze veroorzaakt worden door deze forumsoftware, maar ook ontbreken er enkele stukken om het geheel te doen compileren.
Ik vermoed dat eenvoudig debuggen de oorzaak onmiddellijk aan het licht zal brengen.

#15 Dirk Andries

Dirk Andries

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1189 berichten
    Laatst bezocht
  • LocatieGent

Geplaatst op 17 augustus 2014 - 15:01

Bericht bekijkenjosk79, op 15 augustus 2014 - 15:06, zei:

In onderstaande code kan in zeeeeeeeer uitzonderlijke gevallen een 'race conditie' ontstaan; als vanuit een andere thread de PropertyChanged handler wordt verwijderd, nadat de != null controle is uitgevoerd, maar voordat PropertyChanged(...) wordt aangeroepen, krijg je een null reference exception. Dat is de reden dat je in vele voorbeelden die extra variabele gebruikt ziet worden.

Dat wordt dus her en der aangeraden als een "best practice".
Echter: delegates (waarvan een event gebruik maakt) zijn immutable.
Dat maakt dat bij het aanroepen van een event op een tijdelijke variabele je de race conditie verplaatst naar het target object en/of de target method in de delegate indien ondertussen de eventhandler verwijderd werd uit de InvocationList van de oorspronkelijke delegate (en laat dat nu net de reden zijn waarom die tijdelijke variabele zou moeten gebruikt worden).
De kans is dus nu groter (zeker bij multicast delegates) dat je een ongeldig object probeert te benaderen (detachen van eventhandlers gebeurt niet zelden bij een IDisposable.Dispose bijvoorbeeld).
M.a.w. het gebruik van de  tijdelijke variabele is geen oplossing voor de oorzaak van de fout en verplaatst het probleem gewoon (en ik vrees met een grotere waarschijnlijkheid op fouten bij multicast events).

Beter is dus, denk ik, er voor te zorgen dat de race conditie niet voorkomt en je eventhandlers attachen en detachen in dezelfde thread als die waarin de method die de invocationlist aanspreekt loopt.

#16 josk79

josk79

    Master Developer

  • Leden
  • PipPipPipPipPip
  • 614 berichten
    Laatst bezocht 07 apr 2020 23:55

Geplaatst op 17 augustus 2014 - 16:48

Ik ben het eens met je conclusie ('beter is...') maar toch is de genoemde constructie een manier om aanroepen van een null Delegate te voorkomen.

Dat het event wordt aangeroepen tijdens een ongeldige staat van het object (bijv na Dispose) zou je in de target method zelf kunnen afhandelen, maar is een probleem dat losstaat van de null Delegate.

#17 Dirk Andries

Dirk Andries

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1189 berichten
    Laatst bezocht
  • LocatieGent

Geplaatst op 21 augustus 2014 - 12:28

Bericht bekijkenHypenate, op 17 augustus 2014 - 08:53, zei:

Ik zie vooralsnog geen verschil tussen jouw & mijn programma...behalve dat het mijne niet werkt
Ik heb je solution bekeken (enkel gedeelte UserRole), en ik zie een heleboel verschillen.

Bericht bekijkenHypenate, op 17 augustus 2014 - 08:53, zei:

Is het misschien omdat ik het object aanmaak in een 2e formulier/view?!
Je hebt een stuk of drie instanties van je ViewModel bij het toevoegen van een UserRole.
Dat gaat dus niet echt werken zoals je denkt.
Dat komt omdat je de datacontext meerdere malen instelt (eens als DataContext voor Window en eens als Binding voor Grid).

Bericht bekijkenHypenate, op 15 augustus 2014 - 10:28, zei:

ik heb deze tutorials gevolgd:
https://www.youtube....D29FB131ADF509F
Dat kan ik niet helemaal terugvinden in je code.
Ik vrees dat je enkele details niet helemaal goed hebt.

Vooreerst: je hebt altijd minstens 2  en soms 3 instanties van je UserRoleViewModel (zie hierboven).
Je ViewModel bestuurt meerdere Views, wat dus kan, maar dan lijkt het me logisch dat ze ook dezelfde instantie delen?
Dat doe je niet, waardoor je meerdere instanties hebt die elk twee maal worden geïnstantieerd.
Je maakt telkens nieuwe instanties van dezelfde commands aan. Wat is de redenering daarachter?
Je definieert models. Waarom? Volgens mij zijn die overbodig, want je entities zijn eigenlijk je models, toch?
Je mengt commands en Command Binding met Events in de Views.
Al je assemblies zijn WPF applicaties? Waarom is dat?
Je Command structuur is wel heel vreemd met afzonderlijke namespaces voor Add en Update. Waarom is dat? Waar haal je dat vandaan?
Je hebt een SaveChanges method met daarin een keuze op waarde van van de PK om te beslissen of je toevoegt of update, die wordt aangeroepn door zowel het Add als het Update Command. Dat snap ik niet: waarom laat je het Update Command niet updaten en het Add command niet inserten?


Verder zijn een aantal keuzes die gemaakt worden in die video's en/of door jou niet de mijne zouden zijn:
- IDataErrorInfo en IPropertyChanged interfaces implementeren in Model (i.p.v. in ViewModel, wat ik zou doen).
- Commands houden een instantie van ViewModel bij ( dat kan beter door gebruik te maken van actions).
- Zelf werk ik enkel met POCO's en met Code First in EF.
- Zelf zou ik een EF-agnostic dao/repository interface tussen EF en ViewModel brengen.

Verder vraag ik me ook af wat de reden is waarom je drie verschillende edmx hebt in je DAL?

#18 Dirk Andries

Dirk Andries

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1189 berichten
    Laatst bezocht
  • LocatieGent

Geplaatst op 21 augustus 2014 - 12:44

Bericht bekijkenjosk79, op 17 augustus 2014 - 16:48, zei:

maar toch is de genoemde constructie een manier om aanroepen van een null Delegate te voorkomen.

Ik ben in mijn eerste reactie wat voorzichtig geweest: het voorkomen van het aanroepen van een null delegate door een andere (vorige) instantie aan te roepen is m.i. bad practice.

Dat de delegate null wordt is een bijzonder geval van het algemene geval waar een eventhandler aangeroepen wordt terwijl dat niet meer de bedoeling was. Namelijk het geval waarin die eventhandler gewoon de laatste in de invocation list is.
Dus de truuk met de tijdelijke variabele lost niet alleen het race condition probleem niet op (een bug die moet opgelost worden), het eet bovendien een fout op die de bug signaleert (maar enkel in het bijzondere geval). Het originele (racing condition) probleem wordt in alle gevallen verplaatst naar alle in de tijdelijke delegate nog aanwezige eventhandlers en dat dus om een bug weg te moffelen?

Bericht bekijkenjosk79, op 17 augustus 2014 - 16:48, zei:

Dat het event wordt aangeroepen tijdens een ongeldige staat van het object (bijv na Dispose) zou je in de target method zelf kunnen afhandelen
Het is ook niet beperkt tot na een Dispose: alle eventhandlers (ook in code van derden) worden potentieel slachtoffer van het onterecht aanroepen van de method.
De enige zinnige reactie in beide gevallen is overigens een System.ObjectDisposedException of een System.InvalidOperationException (of dergelijke) op te gooien.
Tenzij je voorstelt om ook daar de fout gewoon te negeren? Wat ik me moeilijk kan voorstellen.

Bericht bekijkenjosk79, op 17 augustus 2014 - 16:48, zei:

maar is een probleem dat losstaat van de null Delegate.
Neen, het staat er helemaal niet los van.
Het probleem wordt net veroorzaakt door het gebruik van een tijdelijke variabele om 1 bijzonder geval (delegate wordt null) op te vangen.
En het is helaas niet enkel in dat uitzonderlijke geval dat je de fout verhuist, maar voor alle gevallen (met race condition).
Dergelijke lapmiddelen in threaded code zijn nefast voor de stabiliteit van software.

Bericht bekijkenjosk79, op 17 augustus 2014 - 16:48, zei:

maar toch is de genoemde constructie een manier om aanroepen van een null Delegate te voorkomen.
Is op zich juist, als statement, maar het is een absoluut slecht idee.

#19 Hypenate

Hypenate

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1228 berichten
    Laatst bezocht 15 mei 2019 21:27
Inzender

Geplaatst op 21 augustus 2014 - 13:33

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Dat komt omdat je de datacontext meerdere malen instelt (eens als DataContext voor Window en eens als Binding voor Grid).
Dit blijft toch dezelfde referentie als ik bovenaan in de XAML heb definieerd?

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Vooreerst: je hebt altijd minstens 2  en soms 3 instanties van je UserRoleViewModel (zie hierboven).
Je ViewModel bestuurt meerdere Views, wat dus kan, maar dan lijkt het me logisch dat ze ook dezelfde instantie delen?
Dat doe je niet, waardoor je meerdere instanties hebt die elk twee maal worden geïnstantieerd.
Dit snap ik niet?

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Je maakt telkens nieuwe instanties van dezelfde commands aan. Wat is de redenering daarachter?
Ik maak deze idd steeds aan in de 'Load' sub aan, ik zal deze dan best verhuizen naar de constructor van de klasse?

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Je definieert models. Waarom? Volgens mij zijn die overbodig, want je entities zijn eigenlijk je models, toch?
Neen, deze worden automatisch gemaakt, dus ook als je iets aanpast hierin overschrijft hij opnieuw alles.

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Je mengt commands en Command Binding met Events in de Views.
Dit snap ik niet?

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Al je assemblies zijn WPF applicaties? Waarom is dat?
Omdat ik graag een volledige opsplitsing had van alle projeten in die solution, er komen er nog bij achteraf.

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Je Command structuur is wel heel vreemd met afzonderlijke namespaces voor Add en Update. Waarom is dat? Waar haal je dat vandaan?
Ik heb deze in apartje mapjes gestoken voor een beter overzicht te bewaren.

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Je hebt een SaveChanges method met daarin een keuze op waarde van van de PK om te beslissen of je toevoegt of update, die wordt aangeroepn door zowel het Add als het Update Command. Dat snap ik niet: waarom laat je het Update Command niet updaten en het Add command niet inserten?
AddCommand geeft een nieuw dialoogje weer, waarna het Update command gaat zien of er een ID is, indien niet, inserten, indien wel, updaten.

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Verder zijn een aantal keuzes die gemaakt worden in die video's en/of door jou niet de mijne zouden zijn:
- IDataErrorInfo en IPropertyChanged interfaces implementeren in Model (i.p.v. in ViewModel, wat ik zou doen).
- Commands houden een instantie van ViewModel bij ( dat kan beter door gebruik te maken van actions).
- Zelf werk ik enkel met POCO's en met Code First in EF.
- Zelf zou ik een EF-agnostic dao/repository interface tussen EF en ViewModel brengen.
Ik heb de indruk dat het MVVM patroon nogal vrij geïnterpreteerd kan worden op vele gebied.

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:28, zei:

Verder vraag ik me ook af wat de reden is waarom je drie verschillende edmx hebt in je DAL?
Ik behandel ze als datasets, waar ik datatables groepeer die bij elkaar horen.

#20 josk79

josk79

    Master Developer

  • Leden
  • PipPipPipPipPip
  • 614 berichten
    Laatst bezocht 07 apr 2020 23:55

Geplaatst op 21 augustus 2014 - 15:59

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:44, zei:

Dat de delegate null wordt is een bijzonder geval van het algemene geval waar een eventhandler aangeroepen wordt terwijl dat niet meer de bedoeling was. Namelijk het geval waarin die eventhandler gewoon de laatste in de invocation list is.

Niet perse. Kan toch ook zijn dat de eventhandler nog net is aangeroepen voor het verwijderen van de delegate ( MyObject.PropertyChanged -= MyObject_PropertyChanged)? En juist omdat je in je klasse geen invloed heb op wat de boze buitenwereld ermee doet, lijkt het me verstandig de nullcheck uit te voeren.

Bericht bekijkenDirk Andries, op 21 augustus 2014 - 12:44, zei:

Het probleem wordt net veroorzaakt door het gebruik van een tijdelijke variabele om 1 bijzonder geval (delegate wordt null) op te vangen.

Het probleem wordt juist veroorzaakt doordat hij immutable is, en bijv. bij de eerste controle "if (MyHandler != null) " een waarde bevat, maar de regel verder " { MyHandler(this, myargs); }" door null is vervangen.

Leuk leesvoer: http://blogs.msdn.co...-and-races.aspx

#21 Hypenate

Hypenate

    Guru Developer

  • Leden
  • PipPipPipPipPipPip
  • 1228 berichten
    Laatst bezocht 15 mei 2019 21:27
Inzender

Geplaatst op 14 september 2014 - 09:33

Ik heb dit kunnen oplossen, weliswaar door het patroon te verbreken... maar het doet wat het moet doen, en misschien dat ik het ooit eens op de volwaardige manier kan doen lukken :)

C Code:
	public partial class UserRoleView : Window
	{
		public UserRoleView()
		{
			InitializeComponent();
		}

		private void btnAdd_Click(object sender, RoutedEventArgs e)
		{
			Views.UserRole.UserRoleAdd frm = new Views.UserRole.UserRoleAdd();
			frm.ShowDialog();

			ViewModels.UserRoleViewModel viewmodel = new ViewModels.UserRoleViewModel();
			this.DataContext = viewmodel;
		}
	}






Ook met taq WPF, MVVM, c# voorzien

0 gebruiker(s) lezen dit onderwerp

0 lid(leden), 0 bezoeker(s), 0 anonieme gebruikers

Inloggen


[WPF] Untitled 1

Met dank aan Jürgen voor de jarenlange inzet van visualbasic.be (anno dec 2000)
Met dank aan Mike en Ronneke voor de jarenlange inzet van vbib.be (anno dec 2010)
Met dank aan PascalBianca voor de jarenlange inzet van vbib.be (anno dec 2016)