|
I'm creating a chess game.
The board is made up of a grid of user controls called BoardSqaure:
<UserControl x:Class="Chess.UI.WPF.Controls.BoardSquare"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Chess.UI.WPF.Controls"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<ContentPresenter Content="{Binding Piece}"/>
</UserControl>
and
namespace Chess.UI.WPF.Controls
{
public partial class BoardSquare : UserControl
{
public BoardSquare()
{
InitializeComponent();
}
public static readonly DependencyProperty PieceProperty =
DependencyProperty.Register("Piece",
typeof(PieceBase),
typeof(BoardSquare),
new PropertyMetadata(null));
public PieceBase Piece
{
get { return (PieceBase)GetValue(PieceProperty); }
set { SetValue(PieceProperty, value); }
}
}
}
The DP Piece will hold an instance of PieceBase, which can be King, Queen, Rook.. etc.
To load the board in the code behind I'm trying to do
private void SetupBoard()
{
BoardSquare square = board.Children.Cast<BoardSquare>().First(e => Grid.GetRow(e) == 0 && Grid.GetColumn(e) == 0);
square.Piece = new RookBlack();
square = board.Children.Cast<BoardSquare>().First(e => Grid.GetRow(e) == 0 && Grid.GetColumn(e) == 1);
square.Piece = new BishopBlack();
.
.
.
}
However the pieces never shows up.
What am I doing wrong here?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
modified 5-Dec-16 17:08pm.
|
|
|
|
|
Kevin Marois wrote: BoardSquare square = board.Children.Cast<BoardSquare>().First(e => Grid.GetRow(e) == 0 && Grid.GetColumn(e) == 0);
square.Piece = new RookBlack();
square = board.Children.Cast<BoardSquare>().First(e => Grid.GetRow(e) == 0 && Grid.GetColumn(e) == 1);
square.Piece = new BishopBlack();
Well first off the bishop doesn't go next to the rook In all seriousness though, I see nothing wrong with the code you've posted. Could you edit with all relevant Queen code? I'm assuming the other pieces are displaying properly?
|
|
|
|
|
No piece that I assign to the Piece property shows up
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
There's really no way to tell from the code posted what's going on. ContentPresenter is generally used in a ControlTemplate . Using it raw like this means there are SOO many variables that could affect what's going on. Is there a DataTemplate associated with the type? What type of element is PieceBase ? Does it have any templates associated with it?
ContentPresenter is a very dynamic class. You can see just how many different things it checks and may or may not apply in the remarks section of its MSDN page[^].
|
|
|
|
|
OK, so what would be the right architecture here for a chess board? Keep in mind that the user will drag/drop pieces around, and pieces will also need to be programmatically added & removed.
Thanks
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
You can programmatically remove anything. For drag and drop, the MSDN article Walkthrough: Enabling Drag and Drop on a User Control[^] has everything you need. As far as architecture, that's entirely a personal choice.
You can derive your UserControl from ContentControl and use a ContentPresenter . You could use Image controls[^] inside each Grid square. I mean WPF is a huge and flexible architecture.
EDIT: I forgot to mention, you can use the ContentPresenter like that - nothing inherently wrong with it. There's just not enough information for me to say why it might not be working without all the code that could affect it.
|
|
|
|
|
There's too many things that you may have accounted for that we can't see:
Width
Height
Visibility
Transparency
Z-Order
Background color
Foreground color
Top
Left
Rendering
...
|
|
|
|
|
In a chess app I wrote some time back: WPF: P2P Chess[^], I took an approach similar to what you're doing. I had a bunch of user controls to represent the pieces. Over time I grew uncomfortable with this and decided to take an MVVM friendly approach for an app I'm currently developing, where the user plays against Stockfish. The chess board in my current app is a ListBox whose ItemsPanel is a Grid with eight row and eight columns. The ItemsSource property of the ListBox is bound to a collection of objects that implement the same interface. The collection, in the View Model, is made up of BoardSquare s and ChessPiece s. The DataTemplate of the ListBox is an Image that uses MultiDataTrigger s to determine the appropriate image to display depending on the type of object. I'll be posting an article soon, maybe tommorrow, where you can dig deeper into the code.
"As beings of finite lifespan, our contributions to the sum of human knowledge is one of the greatest endeavors we can undertake and one of the defining characteristics of humanity itself"
|
|
|
|
|
I look forward to seeing it.
One question... When setting up the board, the board is layout grid with 8 rows & 8 columns, and the pieces are added to the grid during setup.
How do you reverse the board for the other player?
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Kevin Marois wrote: How do you reverse the board for the other player? By reverse I guess you mean flip. I rotate the ListBox and switch its DataTemplate . One DataTemplate has the Image control rotated at an angle of 0 while the other has the Image control rotated at an angle of 180 degrees.
"As beings of finite lifespan, our contributions to the sum of human knowledge is one of the greatest endeavors we can undertake and one of the defining characteristics of humanity itself"
|
|
|
|
|
Check the Visual Studio output window, and you'll probably find a load of binding errors telling you that the property Piece doesn't exist in the data context of the parent window.
Update the user control so that it points to itself for the DataContext :
public BoardSquare()
{
InitializeComponent();
DataContext = this;
}
Or:
<UserControl x:Class="Chess.UI.WPF.Controls.BoardSquare"
...
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
You were right. Thanks!
If it's not broken, fix it until it is.
Everything makes sense in someone's mind.
Ya can't fix stupid.
|
|
|
|
|
Hello!
I am trying to make a Contact Book, where i can have my contacts and conversations(+other activities related to them) in C# WPF.I am also trying to learn MVVM with this.
I have 2 listboxes each bound to an observable collection populated from a database using LinqToSQL.
Listbox 1(ContactsLstBx) is displaying a list of contact names, and in Listbox 2(ConversationLstBx), i want to display the conversations i had with the selected contact in Listbox 1.
The problem i have is that(besides being a "noob") the SelectedContact property, does not update after i set it in MainViewModel constructor. If i set it manually it displays the conversation list correctly based on the ContactId.
What i am trying to achieve is: when the program starts, the only listbox loaded is the ContactsLstBxd after i select a contact, to get the SelectedContact, and load the other listbox.
Should i use a SelectionChanged event? Can i load the conversation listbox if i add a call to PopulateConversationList() in my SelectedContact setter?
Any help/advice is greatly apreciated.
Thanks!
C#:
public class MainViewModel : ViewModelBase
{
private Contact selectedContact;
public ObservableCollection<Contact> ContactList;
public ObservableCollection<Conversation> ConversationList;
public MainViewModel()
{
ContactList = new ObservableCollection<Contact>(cbdc.Contacts);
SelectedContact = ContactList.FirstOrDefault();
PopulateConversationList();
}
public Contact SelectedContact
{
get { return selectedContact; }
set
{
if (selectedContact == value)
return;
selectedContact = value;
RaisePropertyChanged("SelectedContact");
}
}
public void PopulateConversationList()
{
if (selectedContact != null)
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList = new ObservableCollection<Conversation>(conversations);
}
}
public class ViewModelBase: INotifyPropertyChanged
{
public ContactLinqToSQLClassesDataContext cbdc = new ContactLinqToSQLClassesDataContext();
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
XAML:
<ListBox x:Name="ContactsLstBx" HorizontalAlignment="Left" Height="289" Margin="10,50,0,0" VerticalAlignment="Top" Width="115" ItemsSource="{Binding ContactList}" SelectedItem="{Binding SelectedContact, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="ConversationLstBx" HorizontalAlignment="Left" Height="134" Margin="333,50,0,0" VerticalAlignment="Top" Width="115" ItemsSource="{Binding ConversationList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Title}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
|
|
|
|
|
You're replacing the entire conversation list, so you need to make it a property, and call RaisePropertyChanged when you replace it.
You'll also need to call PopulateConversationList when the selected contact changes.
You can simplify the calls to RaisePropertyChanged by using the CallerMemberName attribute[^] on the parameter. You should also store the event handler delegate in a local variable before testing and calling it, since other threads might modify it between the null test and the invocation.
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public class MainViewModel : ViewModelBase
{
private Contact selectedContact;
private ObservableCollection<Contact> contactList;
private ObservableCollection<Conversation> conversationList;
public MainViewModel()
{
contactList = new ObservableCollection<Contact>(cbdc.Contacts);
SelectedContact = contactList.FirstOrDefault();
}
public ObservableCollection<Contact> ContactList
{
get { return contactList; }
}
public Contact SelectedContact
{
get
{
return selectedContact;
}
set
{
if (selectedContact != value)
{
selectedContact = value;
RaisePropertyChanged();
PopulateConversationList();
}
}
}
public ObservableCollection<Conversation> ConversationList
{
get
{
return conversationList;
}
private set
{
if (conversationList != value)
{
conversationList = value;
RaisePropertyChanged();
}
}
}
private void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList = null;
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList = new ObservableCollection<Conversation>(conversations);
}
}
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
Thank You!
I have modified my code, but my SelectedContact still doesnt update.
The XAML code for the listbox bindings remains the same as in my first post.
What should i do next?
(sorry if its something obvious that i havent noticed)
public class MainViewModel : ViewModelBase
{
private Contact selectedContact;
private ObservableCollection<Contact> contactList;
private ObservableCollection<Conversation> conversationList;
public MainViewModel()
{
ContactList = new ObservableCollection<Contact>(cbdc.Contacts);
SelectedContact = ContactList.FirstOrDefault();
}
public ObservableCollection<Contact> ContactList
{
get
{
return contactList;
}
private set
{
if (contactList != value)
{
contactList = value;
RaisePropertyChanged();
}
}
}
public ObservableCollection<Conversation> ConversationList
{
get
{
return conversationList;
}
private set
{
if (conversationList != value)
{
conversationList = value;
RaisePropertyChanged();
}
}
}
public Contact SelectedContact
{
get
{
return selectedContact;
}
set
{
if (selectedContact != value)
{
selectedContact = value;
RaisePropertyChanged();
PopulateConversationList();
}
}
}
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList = null;
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList = new ObservableCollection<Conversation>(conversations);
}
}
}
public class ViewModelBase: INotifyPropertyChanged
{
public ContactLinqToSQLClassesDataContext cbdc = new ContactLinqToSQLClassesDataContext();
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public class ViewModelBase: INotifyPropertyChanged
{
public ContactLinqToSQLClassesDataContext cbdc = new ContactLinqToSQLClassesDataContext();
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
internal void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainViewModel cvm = new MainViewModel();
ContactsLstBx.ItemsSource = cvm.ContactList;
ConversationLstBx.ItemsSource = cvm.ConversationList;
}
}
|
|
|
|
|
ItemsSource binds to a collection. There is a default CollectionView generated that is what you are actually seeing. The CollectionView listens for a CollectionChanged event not a PropertyChanged event. Now, you may assume that ObservableCollection handles all this for you, and generally it does. But you are not performing any actions that are monitorable by this class (Add, Move, Remove, Replace, Reset[^]) and instead are completely replacing the instance itself. Try this:
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList = null;
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList.Clear();
foreach (Conversation c in conversations)
{
ConversationList.Add(c);
}
}
}
I highly recommend anyone learning WPF to read the MSDN Data Binding Overview[^] article. If you skip down to "Binding to Collections" you'll see relevant information for both your question and my answer
Haven't had a chance to actually run the code but this definitely seems like the issue since I've run into the same problem before.
|
|
|
|
|
I have modified the PopulateConversationList() method, and before
ConversationList.Clear();
i have added
if (ConversationList == null)
{
ConversationList = new ObservableCollection<Conversation>();
}
because i was getting a NullRefferenceException.
The code works as before(SelectedContact doesnt updates).
What else should i check?
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList = null;
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
if (ConversationList == null)
{
ConversationList = new ObservableCollection<Conversation>();
}
ConversationList.Clear();
foreach (Conversation c in conversations)
{
ConversationList.Add(c);
}
}
|
|
|
|
|
The whole point of my post was to remove the re-assignment. Unless you want to implement your own CollectionChanged event to cover the re-assignment, the default CollectionView won't know the collection was modified. I forgot to change the null assignment in your if-statement. Also, you should instantiate in your constructor.
public MainViewModel()
{
ContactList = new ObservableCollection<Contact>(cbdc.Contacts);
ConversationList = new ObservableCollection<Conversation>();
SelectedContact = ContactList.FirstOrDefault();
PopulateConversationList();
}
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList.Clear();
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList.Clear();
foreach (Conversation c in conversations)
{
ConversationList.Add(c);
}
}
If you really want to remake the collection each time you can but you will have to implement INotifyCollectionChanged to cover your re-assignment which is not as friendly as INotifyPropertyChanged . That's a lot of extra mess when you can simply re-use the collection like this.
|
|
|
|
|
I have modified the code and i get the same results as before.
Sorry, i dont want to remake the collection, but i am a little(maybe a little more) confused.
Should i check something else?
public class MainViewModel : ViewModelBase
{
private Contact selectedContact;
private ObservableCollection<Contact> contactList;
private ObservableCollection<Conversation> conversationList;
public MainViewModel()
{
ContactList = new ObservableCollection<Contact>(cbdc.Contacts);
ConversationList = new ObservableCollection<Conversation>();
SelectedContact = ContactList.FirstOrDefault();
PopulateConversationList();
}
public ObservableCollection<Contact> ContactList
{
get
{
return contactList;
}
private set
{
if (contactList != value)
{
contactList = value;
RaisePropertyChanged();
}
}
}
public Contact SelectedContact
{
get
{
return selectedContact;
}
set
{
if (selectedContact != value)
{
selectedContact = value;
RaisePropertyChanged();
PopulateConversationList();
}
}
}
public ObservableCollection<Conversation> ConversationList
{
get
{
return conversationList;
}
private set
{
if (conversationList != value)
{
conversationList = value;
RaisePropertyChanged();
}
}
}
public void PopulateConversationList()
{
if (selectedContact == null)
{
ConversationList.Clear();
}
else
{
var conversations = from c in cbdc.Conversations
where c.ContactID == SelectedContact.Id
select c;
ConversationList.Clear();
foreach (Conversation c in conversations)
{
ConversationList.Add(c);
}
}
}
}
|
|
|
|
|
So I mocked up a DB and used your code with some optimizations I explain in comments. Works for me. If you're still having an issue it might be a DB issue or some code that isn't posted. Here's the code I used:
MainWindow.xaml: Nothing important changed, just some formatting for my screen
<Window x:Class="ContactsList.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<DockPanel LastChildFill="True">
<ListBox x:Name="ContactsLstBx" Width="200" ItemsSource="{Binding ContactList}" SelectedItem="{Binding SelectedContact, UpdateSourceTrigger=PropertyChanged}" DockPanel.Dock="Left">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox x:Name="ConversationLstBx" Width="200" ItemsSource="{Binding ConversationList}" DockPanel.Dock="Right" HorizontalAlignment="Right" >
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Title}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainViewModel ViewModel { get; private set; }
public MainWindow()
{
InitializeComponent();
ViewModel = new MainViewModel();
DataContext = ViewModel;
}
}
MainViewModel.cs
public class MainViewModel : ViewModelBase
{<br />
public ObservableCollection<Contact> ContactList { get; private set; }
public ObservableCollection<Conversation> ConversationList { get; private set; }
private Contact selectedContact;
public Contact SelectedContact
{
get
{
return selectedContact;
}
set
{
if (selectedContact != value)
{
selectedContact = value;
PopulateConversationList();
RaisePropertyChanged();
}
}
}
public MainViewModel()
{
ContactList = new ObservableCollection<Contact>(cbdc.Contacts);
ConversationList = new ObservableCollection<Conversation>();
SelectedContact = ContactList.FirstOrDefault();
}
public void PopulateConversationList()
{
ConversationList.Clear();
if (selectedContact != null)
{
var conversations = cbdc.GetConversations(selectedContact);
foreach (Conversation c in conversations)
ConversationList.Add(c);
}
}
}
ViewModelBase.cs Not really important, just me mocking up DB objects
public class ViewModelBase : INotifyPropertyChanged
{
protected MockDB cbdc;
public event PropertyChangedEventHandler PropertyChanged;
public ViewModelBase()
{
cbdc = new MockDB();
}
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class MockDB
{
private Dictionary<Contact, List<Conversation>> conversations;
public List<Contact> Contacts { get; private set; }
public MockDB()
{
Contacts = CreateContacts();
conversations = CreateConversations(Contacts);
}
public List<Conversation> GetConversations(Contact c) =>
conversations[c];
private List<Contact> CreateContacts()
{
return new List<Contact>()
{
new Contact("Jim"),
new Contact("Bob"),
new Contact("Mary")
};
}
private Dictionary<Contact, List<Conversation>> CreateConversations(List<Contact> contacts)
{
Dictionary<Contact, List<Conversation>> conversationMap = new Dictionary<Contact, List<Conversation>>();
foreach (Contact c in contacts)
conversationMap.Add(c, CreateConversationList(c));
return conversationMap;
}
private List<Conversation> CreateConversationList(Contact contact)
{
return new List<Conversation>()
{
new Conversation($"Hi {contact.Name}!"),
new Conversation("Pleasant day today.")
};
}
}
public class Contact
{
public string Name { get; private set; }
public Contact(string contactName)
{
Name = contactName;
}
}
public class Conversation
{
public string Title { get; private set; }
public Conversation(string conversationText)
{
Title = conversationText;
}
}
Hopefully this helps
|
|
|
|
|
Thank you very much, it works!
I wasnt setting the datacontext right(i was thinking that the datacontext generated by my LinqToSql classes was the only one i have to interact with).
Thank you again!
|
|
|
|
|
Richard Deeming wrote: You should also store the event handler delegate in a local variable before testing and calling it, since other threads might modify it between the null test and the invocation.
If C# 6.0 is available in your environment this is no longer necessary with the new null-conditional operator. Just an fyi
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
|
|
|
|
|
|
Thank You!
I will look into this.
|
|
|
|
|
I have a WPF application which consists of a main window containing a TabControl whose items are populated dynamically from the model. This is a simplified version:
<Window x:Class="WpfDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:view="clr-namespace:WpfDemo.View"
xmlns:viewmodel="clr-namespace:WpfDemo.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
Title="MainWindow" Height="350" Width="525" Background="#DCE8F3">
<Grid>
<TabControl Grid.Row="0" x:Name="ViewTabControl" ItemsSource="{Binding ViewModels}" SelectedItem="{Binding SelectedTab, Mode=TwoWay}" IsSynchronizedWithCurrentItem="True" Background="LightBlue" Margin="6">
<TabControl.Resources>
<DataTemplate DataType="{x:Type viewmodel:ImageCaptureViewModel}">
<view:ImageCaptureView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodel:ProfileDesignerViewModel}">
<view:ProfileDesignerView/>
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</Grid>
</Window>
The view model class - MainWindowViewModel - has an observable collection called ViewModels:
private ObservableCollection<Object> _viewModels;
public ObservableCollection<Object> ViewModels
{
get
{
if (_viewModels == null)
{
_viewModels = new ObservableCollection<Object>();
}
return _viewModels;
}
}
The views are created thus:
public MainWindowViewModel()
{
_profileDesignerViewModel = new ViewModel.ProfileDesignerViewModel();
ViewModels.Add(_profileDesignerViewModel);
_imageCaptureViewModel = new ViewModel.ImageCaptureViewModel();
ViewModels.Add(_imageCaptureViewModel);
}
So far so good. Unfortunately this has a peculiarity. A view is instantiated each time the user selects the corresponding tab. The problem is that the old view is not disposed until the application shuts down.
Does anyone know how to ensure that the old view is disposed, or how to keep the old view and not create a new one? This is creating a serious memory problem for us.
|
|
|
|