views:

140

answers:

3

Lets say I have an application which comprises one window. Inside the window is a tabcontrol, with three tabitems. Inside each tabitem is a usercontrol.

In one tab I have Add color. In the next tab I have add Fruit. In the third tab I have relationships, where the user can add links between the fruit and the colors. This relationship is displayed in a listbox like:

Apple > Red
Pear  > Green

Below this in the same tab I have missing fruits listbox and a missing colors listbox... i.e. fruits or colors that have been added but not linked in the relationship. I should explain that this data is all stored in three seperate textfiles, fruits.txt, colors.txt and relationships.txt.

The problem I have is as follows. At the moment the populating of the listboxes etc is on Usercontrol_loaded event. It doesn't matter for fruit/colors, as after the add button is clicked, the list reloads. The problem is in the relationship screen.

Say the user runs the program, and clicks on the relationship tab to see what is linked. We'll say that the above example was already in the textfile. So that relationship shows up - and no missing fruits. The user then clicks the fruit tab and adds a fruit and then the color tab and adds a color. Then moves to the relationship tab. The usercontrol_loaded event has already occured, so these two new additions do not show in the listboxes.

If I move the code to GotFocus event, the user can't make selection in any listbox because it is constantly loading, as clicking fires the event.

Is there any other event or way I could have this refresh JUST when the tab has been "switched to", other than providing a refresh button?

Thanks for reading.

...

Edit 1: If I databind to a list of missing fruits which I build in the codebehind, I still have the same problem. I have to reload this list everytime they go jump off this tab and come back to it (because potentially they've added a fruit or a color, which is now missing).

A: 

You need to bind your listboxes to the data from your ViewModel. This way the WPF framework overtakes the detection of the moment when to update the data.

Vlad
but there is logic to work out which fruits for example are not in the relationship file. How do you databind that
baron
you would need a separate list for them, so you are binding your missing fruit to the list of missing fruit. you would need to update the lists whenever some of the data change: user reloads the data, or makes additional relationship, etc.
Vlad
Then it's the same problem! The logic to check the list - where does it go? Usercontrol_load - then the list im binding to is always the same. GotFocus - then its not selectable. Unless you are saying I have to do the binding in the xaml - at the moment I have it in UserControl_Loaded { listbox.ItemsSource = missingFruits; } missingFruits being ObservableCollection<string>
baron
If i remove the code behind itemssource and set in xaml like so: <ListBox Name="listbox" ItemsSource="{Binding missingFruits}"/> it doesn't work. I love databinding.
baron
Seriously, as soon as you get a full understanding of data binding, you will worship it. I thought WPF was inconceivably difficult before I understood data binding and MVVM; now I think it's astonishingly easy.
Robert Rossney
A: 

WPF is not WinForms. You don't populate ListBoxes by yourself.

You have

class MyData{
   ObservableCollection<Color> Colors;
   ObservableCollection<Fruit> Fruits;
   ObservableCollection<Pairs> Pairs;

   public void MatchCurrentSelection(){
        var selectedColor = CollectionViewSource.GetDefaultView(Colors).CurrentItem;
        var selectedFruit = CollectionViewSource.GetDefaultView(Fruits).CurrentItem;
        if(selectedColor != null && selectedFruit != null){
             Colors.Remove(selectedColor);
             Fruits.Remove(selectedFruit);
             Pairs.Add(new Pair(selectedColor, selectedFruit));
        }
   }
} 

ListBoxes

<ListBox ItemsSource="{Binding Colors}" IsSynchronizedWithCurrentItem="true"/>
<ListBox ItemsSource="{Binding Fruits}" IsSynchronizedWithCurrentItem="true"/>
<ListBox ItemsSource="{Binding Pairs}"/>
See my comments to vlad. This is not working for me.
baron
+1  A: 

You need to understand how MVVM and change notification works. You don't need to use events or code-behind at all if you're tracking all of this stuff in observable collections in view model classes that are bound to the UI.

As wwosik suggests, create a class that exposes public Colors, Fruits, and Relationships properties that are all observable collections. As s/he didn't suggest but probably should have, also expose public MissingColors and MissingFruits observable collections. Finally, expose public SelectedColor and SelectedFruit properties.

Create bound controls:

<ListBox ItemsSource="{Binding Colors}" SelectedItem="{Binding SelectedColor}"/>
<ListBox ItemsSource="{Binding Fruits}" SelectedItem="{Binding SelectedFruit}"/>
<ListBox ItemsSource="{Binding Relationships}"/>
<ListBox ItemsSource="{Binding MissingColors}"/>
<ListBox ItemsSource="{Binding MissingFruits}"/>

Implement an AddRelationship method that adds a new relationship consisting of the SelectedColor and SelectedFruit. It should also remove the color and fruit from the respective MissingColors and MissingFruits collections. Create a command that calls this method and bind it to something in the UI.

That's it. It doesn't matter what tab anything is on. It doesn't matter what order the user views things in. When the user adds a relationship, or a fruit, or a color, the UI will get updated.

Robert Rossney
So I guess you are implying that the add fruit/color method changes the missing fruit/color lists. I also wonder how the line {Binding Fruits} recognises which class this property is in? I had assumed these had to sit in the codebehind of whatever was trying to display them, but from what i'm slowly understanding from these replies is this is definetly not the case? cheers
baron
I'm not merely implying that, I'm saying it explicitly (see sentence 2, paragraph 4). There are a bunch of ways to make the binding know which class the property is in, but by far the simplest is to set the `DataContext` on whatever object contains all of the bound controls to an instance of the class. If your class's constructor initializes itself, it's as simple as `<object.DataContext><local:MyClass/></object.DataContext>`.
Robert Rossney
: ) - Thank you, you've made it alot clearer
baron