开发者

MVVM Binding a combobox

开发者 https://www.devze.com 2023-02-23 03:57 出处:网络
I have a very ordinary View开发者_开发百科Model and I am tring to bind a collection of values to a combobox. The problem is nothing is binding. I have checked the ViewModel constructor and the data is

I have a very ordinary View开发者_开发百科Model and I am tring to bind a collection of values to a combobox. The problem is nothing is binding. I have checked the ViewModel constructor and the data is being loaded so I suspect its in my XAML but I just cant find out where.

public class OwnerOccupierAccountViewModel : ViewModelBase
{
    readonly UserAccountContext _userAccountContext;
    readonly LoadOperation<Structure> _loadStructures;


    #region Properties

    private ObservableCollection<Structure> _structures;
    public ObservableCollection<Structure> Structures
    {
        get { return _structures; }

        set
        {
            _structures = value;
            RaisePropertyChanged("Structures");
        }
    }

    private Structure _selectedStructure;
    public Structure SelectedStructure
    {
        get { return _selectedStructure; }
        set
        {
            _selectedStructure = value;
            RaisePropertyChanged("SelectedStructure");
        }
    }

    #endregion

    public OwnerOccupierAccountViewModel()
    {
        _userAccountContext = new UserAccountContext();

        if (!DesignerProperties.IsInDesignTool)
        {
            _loadStructures = _userAccountContext.Load(_userAccountContext.GetStructuresQuery());
            _loadStructures.Completed += new EventHandler(_loadStructures_Completed);
        }
    }

    void _loadStructures_Completed(object sender, EventArgs e)
    {
        _structures = new ObservableCollection<Structure>();
        foreach (var structure in _loadStructures.Entities)
        {
            Structures.Add(structure);
        }
    }
}

<UserControl.Resources>
    <viewmodel:OwnerOccupierAccountViewModel x:Key='ViewModel'></viewmodel:OwnerOccupierAccountViewModel>
</UserControl.Resources>

<ComboBox x:Name='cboApartments'
                          ItemsSource='{Binding Structures,Source={StaticResource ViewModel},Mode=TwoWay}'
                          Width='200' />


Try intializing your ObservableCollection of Structures like that:

void _loadStructures_Completed(object sender, EventArgs e)
    {
        Structures = new ObservableCollection<Structure>(_loadStructures.Entities);      
    }

and as it was mentioned earlier i think you should change order here:

if (!DesignerProperties.IsInDesignTool)
{
    //other code before
    //_loadStructures = ...
    _loadStructures.Completed += new EventHandler(_loadStructures_Completed);
   //and now start loading       
}

I did similar, very simple app to check what could gone wrong, but everything works well. I will show you my code, so you can compare and maybe you will find some bugs in your solution.

Structure.cs

public class Structure
{
    public Structure(string name)
    {
        Name = name;
    }
    public string Name { get; set; }
}

StructureService.cs

public class StructureService
{
    public void GetAllStructures(Action<IList<Structure>> CompleteCallback)
    {
        var temp = new List<Structure>() 
            {
                new Structure("Str1"),
                new Structure("Str2"),
                new Structure("Str3"),
                new Structure("Str4"),
                new Structure("Str5"),
                new Structure("Str6"),
                new Structure("Str7")
            };
        CompleteCallback(temp);
    }
}

ViewModelBase.cs

public class ViewModelBase : INotifyPropertyChanged
{
    protected void RaisePropertyChanged(string prop)
    {
        var temp = PropertyChanged;
        if (temp != null)
        {
            temp(this, new PropertyChangedEventArgs(prop));
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
}

OwnerOccupierAccountViewModel:

public class OwnerOccupierAccountViewModel : ViewModelBase
{
    StructureService service;
    public OwnerOccupierAccountViewModel()
    {
        if (!DesignerProperties.IsInDesignTool)
        {
            service = new StructureService();
            service.GetAllStructures((result) =>
            {
                Structures = new ObservableCollection<Structure>(result);
            });

        }
    }
    private ObservableCollection<Structure> _structures;
    public ObservableCollection<Structure> Structures
    {
        get { return _structures; }
        set
        {
            _structures = value;
            RaisePropertyChanged("Stuctures");
        }
    }
    private Structure _selectedStructure;
    public Structure SelectedStructure
    {
        get { return _selectedStructure; }
        set
        {
            _selectedStructure = value;
            RaisePropertyChanged("SelectedStructure");
        }
    }
}

MainPage.xaml:

<UserControl x:Class="SilverlightApplication1.MainPage"
    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"
    xmlns:vm="clr-namespace:SilverlightApplication1"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.Resources>
        <vm:OwnerOccupierAccountViewModel x:Key="ViewModel"/>
    </UserControl.Resources>
        <Grid x:Name="LayoutRoot" Background="White">
        <ComboBox x:Name="cboApartments"
                          ItemsSource='{Binding Structures,Source={StaticResource ViewModel},Mode=TwoWay}'
                          SelectedItem="{Binding SelectedStructure, Source={StaticResource ViewModel},Mode=TwoWay}"
                          Width="100" Height="30">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=Name}"/>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </Grid>
</UserControl>

If i were in your shoes i wll change xaml to such view:

SuggestedView:

<UserControl x:Class="SilverlightApplication1.MainPage"
    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"
    xmlns:vm="clr-namespace:SilverlightApplication1"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400">
    <UserControl.DataContext>
        <vm:OwnerOccupierAccountViewModel/>
    </UserControl.DataContext>
        <Grid x:Name="LayoutRoot" Background="White">
        <ComboBox x:Name="cboApartments"
                          ItemsSource='{Binding Structures, Mode=TwoWay}'
                          SelectedItem="{Binding SelectedStructure, Mode=TwoWay}"
                          Width="100" Height="30">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Path=Name}"/>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>
    </Grid>
</UserControl>

but i understand that it is somehow impossible in your scenario?

MVVM Binding a combobox


Try replacing this line:

 _structures = new ObservableCollection<Structure>();

with this:

Structures = new ObservableCollection<Structure>();

And set the binding of ComboBox to OneWay.

Edited to update solution:

Set DisplayMemberPath property of ComboBox as well:

DisplayMemberPath="StructureName"


The binding will only fire when the property is changed. The line setting the backing variable won't call the RaisePropertyChanged event. Even if it did it would be empty at this point anyway and you'd end up with an empty list.

_structures = new ObservableCollection<Structure>();

When you then add to the collection you aren't changing the property value, you're calling the getter so again the RaisePropertyChanged won't fire.

Structures.Add(structure);

You need to build a local collection then use that as the value for the Structures property. This should cause the binding to be triggered.

var structures = new ObservableCollection<Structure>();
foreach ...
Structures = structures;


You are binding directly to the ViewModel key as a source, but is it set as a DataContext anywhere?

0

精彩评论

暂无评论...
验证码 换一张
取 消