質問

I've found many answers to this question, but I can't seem to get the syntax right. I've tried every logical combination I can think of given the explanation here and the many others I've found but it has still eluded me after three weeks. I just need to get the syntax of the XAML right.

Classes: (renamed for simplicity/confidentiality)

UserControl1 - Contains three global lists, called Streets, Houses, and Cars

Street - Contains two lists of only the associated Houses and Cars, called MyHouses and MyCars

House - Presented in a DataGrid, with one column being a DataGridComboboxColumn to choose which Street this House is associated with. Has a Street property called Street declared in it to keep track of this and do other calculations in get/set.

Car - Presented in a DataGrid, with one column being a DataGridComboboxColumn to choose which Street this Car is associated with. Has a Street property called Street declared in it to keep track of this and do other calculations in get/set.

If requested, I can refactor the code behind to match the above and post it.

XAML

<DataGrid ItemsSource="{Binding Streets, Mode=TwoWay}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Street"  Binding="{Binding StreetID, Mode=TwoWay}"/>
    </DataGrid.Columns>
</DataGrid>
<DataGrid ItemsSource="{Binding Cars, Mode=TwoWay}">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding CarID, Mode=TwoWay}"/>
        <DataGridComboBoxColumn 
        ItemsSource="{Binding Streets, RelativeSource={RelativeSource FindAncestor,AncestorType=UserControl1}, Mode=OneWay}"
        SelectedItemBinding="{Binding Street}"
        SelectedValue="{Binding StreetID}"
        SelectedValuePath="StreetID" 
        DisplayMemberPath="StreetID">
            <DataGridComboBoxColumn.ElementStyle>
                <Style TargetType="ComboBox">
                    <Setter Property="ItemsSource" Value="{Binding Streets, RelativeSource={RelativeSource FindAncestor,AncestorType=UserControl1}, Mode=OneWay}"/>
                    <Setter Property="IsReadOnly" Value="True"/>
                </Style>
            </DataGridComboBoxColumn.ElementStyle>
            <DataGridComboBoxColumn.EditingElementStyle>
                <Style TargetType="ComboBox">
                    <Setter Property="ItemsSource" Value="{Binding Streets, RelativeSource={RelativeSource FindAncestor,AncestorType=UserControl1}, Mode=OneWay}"/>
                </Style>
            </DataGridComboBoxColumn.EditingElementStyle>
        </DataGridComboBoxColumn>
    </DataGrid.Columns>
</DataGrid>
<DataGrid ItemsSource="{Binding Houses, Mode=TwoWay}">
    <!--copy of Cars with changed names-->
</DataGrid>

役に立ちましたか?

解決

If I understand what you're trying to do correctly, I think this is what you're after (based on my mockup from your description):

Note some of my variables have slightly different names due to my mockup e.g. UserControl.

<DataGridComboBoxColumn 
    SelectedItemBinding="{Binding Street}"
    DisplayMemberPath="StreetName">
    <DataGridComboBoxColumn.ElementStyle>
        <Style TargetType="ComboBox">
            <Setter Property="ItemsSource" Value="{Binding Streets, RelativeSource={RelativeSource FindAncestor,AncestorType=UserControl}, Mode=OneWay}"/>
            <Setter Property="IsReadOnly" Value="True"/>
         </Style>
    </DataGridComboBoxColumn.ElementStyle>
    <DataGridComboBoxColumn.EditingElementStyle>
        <Style TargetType="ComboBox">
            <Setter Property="ItemsSource" Value="{Binding Streets, RelativeSource={RelativeSource FindAncestor,AncestorType=UserControl}, Mode=OneWay}"/>
        </Style>
     </DataGridComboBoxColumn.EditingElementStyle>
 </DataGridComboBoxColumn>

For me, this displays my Cars in a datagrid, with all the relevant columns, and selectable streets (you can adapt for Houses in a similar way).

As I think you've read, DataGridComboBoxColumn is a bit odd about resolving its DataContext. When you're trying to set the ItemsSource at the top of the DataGridComboBoxColumn, it's causing confusion.

this should work for the example you've described (which I've modelled with Cars and Houses having a Street object as the tracking property, rather than an ID).

If you decide you want to incorporate SelectedValue and use the returned Street ID (to store as ID properties on Car and House, I think you'll have to do it with another setter in your style templates.

Edit: The Mockup I used (Datacontext set to self in XAML). N.B the objects won't track each other (as I wasn't sure what set up you had).

public partial class UserControl1 : UserControl
{
    public List<Street> Streets { get; set; }
    public List<House> Houses { get; set; }
    public List<Car> Cars { get; set; }

    public UserControl1()
    {
        Streets = new List<Street>();
        Houses = new List<House>();
        Cars = new List<Car>();

        Street streetOne = new Street() { StreetID = 1, Name = "Street One" };
        Street streetTwo = new Street() { StreetID = 1, Name = "Street Two" };
        Car carOne = new Car() { CarID = 1, Name = "KITT", MyStreet = streetOne };
        Car carTwo = new Car() { CarID = 2, Name = "Car 2", MyStreet = streetTwo };
        House houseOne = new House() { HouseID = 1, Name = "House 1", MyStreet = streetOne };

        InitializeComponent();

        streetOne.MyCars.Add(carOne);
        streetOne.MyHouses.Add(houseOne);
        streetTwo.MyCars.Add(carTwo);
        Cars.Add(carOne);
        Cars.Add(carTwo);
        Houses.Add(houseOne);
        Streets.Add(streetOne);
        Streets.Add(streetTwo); 
    }
}

public class Car
{
    public int CarID { get; set; }
    public string Name { get; set; }
    private Street _street;
    public Street MyStreet
    {
        get { return this._street; }
        set { this._street = value; }
    }
}

public class House
{
    public int HouseID { get; set; }
    public string Name { get; set; }
    public Street MyStreet { get; set; }
}

public class Street
{
    public int StreetID { get; set; }
    public string Name { get; set; }
    public List<House> MyHouses { get; set; }
    public List<Car> MyCars { get; set; }

    public Street()
    {
        MyHouses = new List<House>();
        MyCars = new List<Car>();
    }
}

他のヒント

Despite that the global lists are ObservableCollections, they do not appear to be firing PropertyChangedEvents during the constructor of the code behind. I set the ItemsSource in the code behind after adding things and it seems to work fine now. What really makes no sense is that the property changed events are fired once the Streets show up in the House and Car objects because both are aware of each other's changes. Using the setup that Chris posted in the code behind worked, so I am marking his post as the answer. I have no idea why the lists are empty when only binding with that setup in the XAML.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top