views:

593

answers:

2

I've tried to search for an answer to this but I'm not having any luck. Basically I have a listview that is bound to a collection returned from a view model. I bind the selected item of the list view to a property in my listview in order to perform validation to ensure that an item is selected. The problem is that sometimes I want to load this listview with one of the items already selected. I was hoping to be able to set the property on my view model with the object I want selected and have it automatically select that item. This is not happening. My listview loads without an item selected. I can successfully set the selected index to the 0th index so why shouldn't I be able to set the selected value. The list view is in single selection mode.

Here's the pertinent code from my list view

<ListView Name="listView1" ItemsSource="{Binding Path=AvailableStyles}" SelectionMode="Single">
            <ListView.SelectedItem>
                <Binding Path="SelectedStyle" ValidatesOnDataErrors="True" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" BindingGroupName="StyleBinding" >

                </Binding>
            </ListView.SelectedItem>
            <ListView.View>
                <GridView>
                    <GridViewColumn Header="StyleImage">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate>
                               <Image Source="800.jpg"/>
                            </DataTemplate>
                        </GridViewColumn.CellTemplate>
                    </GridViewColumn>
                    <GridViewColumn Header="Style Code" DisplayMemberBinding="{Binding StyleCode}"/>
                    <GridViewColumn Header="Style Name" DisplayMemberBinding="{Binding StyleName}"/>
                </GridView>
            </ListView.View>
        </ListView>

And here is the pertinent code from my view model

public class StyleChooserController : BaseController, IDataErrorInfo, INotifyPropertyChanged
{
    private IList<Style> availableStyles;
    private Style selectedStyle;

    public IList<Style> AvailableStyles 
    {
        get { return availableStyles; }
        set
        {
            if (value == availableStyles)
                return;

            availableStyles = value;

            OnPropertyChanged("AvailableStyles");
        }
    }
    public Style SelectedStyle 
    {
        get { return selectedStyle; }
        set
        {
            //if (value == selectedStyle)
            //    return;

            selectedStyle = value;

            OnPropertyChanged("SelectedStyle");
        }
    }

    public StyleChooserController()
    {
        AvailableStyles = StyleService.GetStyleByVenue(1);

        if (ApplicationContext.CurrentStyle != null)
        {
            SelectedStyle = ApplicationContext.CurrentStyle;
        }
    }

    public string Error
    {
        get { return null; }
    }

    public string this[string columnName]
    {
        get
        {
            string error = string.Empty;
            if (columnName == "SelectedStyle")
            {
                if (SelectedStyle == null)
                {
                    error = "required";
                }
            }

            return error;
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }

}

I should note that the "Style" referenced here has nothign to do with WPF. It's a business object. I'm really looking for a solution that doesn't break the MVVM pattern, but I'd be willing to just get something functioning. I've attempted to loop through the Listview.Items list just to set it manually but it's always empty when I try. Any help is appreciated.

Edit: I updated the code to use INotifyPropertyChanged. It's still not working. Any other suggestions 2nd Edit: I added UpdateSourceTrigger="PropertyChanged". That still did not work.

Thanks

A: 

Hmm... it looks like you forgot to implement INotifyPropertyChanged for the SelectedStyle property...

Anvaka
Thanks for pointing that out. I did try that and it's still not working. I changed my auto-implemented properties to have explicit private vars and trigger the event in the set property. Still doesn't highlight or change the selected value. This is very strange because when you suggested that I thought it would work for sure. I debugged through to make sure the event was firing.
Chris Cap
I just stepped through a bit closer and the handler is null so it's not wiring up the event. I'm not exactly sure why. I thought by specifying a binding that it was automatic. I apologize as this is newbish but your help is appreciated.
Chris Cap
+1  A: 

Your problem is most likely caused because your SelectedItem Style is a different Style instance than the matching one in the AvailableStyles in the ItemsSource.

What you need to do is provide your specific definition of equality in your Style class:

public class Style: IEquatable<Style>
{
    public string StyleCode { get; set; }
    public string StyleName { get; set; }
    public virtual bool Equals(Style other)
    {
        return this.StyleCode == other.StyleCode;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as Style);
    }
}
b-rad
Awesome... That did indeed work. Is there a better approach that I'm unaware? This isn't a bad solution, but it makes me thing I'm doing something wrong. The main reason I made a seperate property was to be able to validate that it was selected in the view model. Thanks again for your help.
Chris Cap
Your approach is fine. Implementing IEquatable like this is good practice for your model objects. Your only alternative to this is to ensure that the SelectedItem object instance is contained in ItemsSource collection (reference equality). This may require a different approach to object loading in your application.
b-rad