相关文章推荐
稳重的企鹅  ·  mysql ...·  1 年前    · 

I have learnt a lot from Andy O'Neill's WPF: Entity Framework MVVM Walk Through 2 example as I learn WPF and MVVM etc. Generally though I always seem to struggle on comboboxes and getting the ItemsSource, SelectedValue and SelectedValuePath set up correctly to successfully show data in the combobox. The Binding is really tricky in combination with the particular comboxbox control design. In Andys example I was trying to put a combobox inside an ItemsControl thats inside a ContentControl (Popup) thats inside a grid on a UserControl. I cannot get the data from MyObservableCollection to show in the ItemsSource. I have tried all manner of versions of comboc control construction and Binding including {Binding DataContext.MyObservableCollection, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl} , RelativeSource={RelativeSource AncestorType={x:Type local:UserControl}} and replacing UserControl with the ViewModels , and using FindAncestor etc. The DataContext is set to the ViewModel where the observable collection is and its got data in it, but seems cant get binding to work.

Any tips to look at for this type of situation would be greatly appreciated or even an answer from the great man himself Mr Andy O'Neill ( anonymous user ONeill) . wow imagine that

thanks
Scott

Great man?

Bear in mind that was not intended to be the final in the walk through series.
I moved on to other things.

In particular:

You should copy data from an entity object to a viewmodel to edit/display.
Then back to an entity object for any update.

I use automapper to do this.

( The sample uses buddy classes for dataattributes and partial inheritance so you can work with entity objects. )

I also use a collection of predicates for validation which (inevitably) doesn't quite fit in with attributes.

HaHa the great man reference worked you replied. Well to be serious I can take this opportunity to say a big thanks for this example it has been very helpful to understand what the overall layout, structure of an app can look like. Most other examples were of just code snippets on how to do a particular thing but this was perfect for my level as it gave the overall view and direction on how to build the whole app.

But hey no reply on my little problem! Cant stick a combobox on the Popup and populate it from an observable collection in the List View Model. Seemed so simple.

I understand you must be very busy if you cant give me a tip on it. Peter has been guide enough to try and help.

thanks again looking forward to Step 3, 4, 5, 6...

Scott

Hi Scott,
in following demo you can see how to bind ComboBoxColumn in DataGrid in UserControl.

XAML MainWindow:

<Window x:Class="Window064"  
        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:local="clr-namespace:WpfApp1.WpfApp064"  
        xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"  
        mc:Ignorable="d"  
        Title="Demo ComboBox in UserControl" Height="450" Width="800">  
  <Window.DataContext>  
    <local:ViewModel/>  
  </Window.DataContext>  
    <uc:Window064UC1/>  
  </Grid>  
</Window>  

ViewModel as DataContext in MainWindow (and UserControl), View property for rows in DataGrid, MasterList as Lookup values for ItemsSource in DataGridComboBoxColumn, LoadDemoData for loading data (e.g. EF).

Imports System.Collections.ObjectModel  
Imports System.ComponentModel  
Namespace WpfApp064  
  Public Class ViewModel  
    Public Sub New()  
      LoadDemoData  
    End Sub  
    Private cvsChild As New CollectionViewSource  
    Private colChild As New ObservableCollection(Of Data)  
    Public ReadOnly Property View As ICollectionView  
        Return cvsChild.View  
      End Get  
    End Property  
    Private colMaster As New ObservableCollection(Of Master)  
    Public ReadOnly Property MasterList As ObservableCollection(Of Master)  
        Return colMaster  
      End Get  
    End Property  
    Private Sub LoadDemoData()  
      Dim rnd As New Random  
      For i = 1 To 10  
        colMaster.Add(New Master With {.ID = i, .MasterInfo = $"Master {i}"})  
      For i = 1 To 100  
        colChild.Add(New Data With {.ID = i, .Info = $"Row {i}", .FKMaster = rnd.Next(1, 11)})  
      cvsChild.Source = colChild  
    End Sub  
  End Class  
  Public Class Data  
    Public Property ID As Integer  
    Public Property FKMaster As Integer  
    Public Property Info As String  
  End Class  
  Public Class Master  
    Public Property ID As Integer  
    Public Property MasterInfo As String  
  End Class  
End Namespace  

XAML UserControl (no CodeBehind)

<UserControl x:Class="Window064UC1"  
             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:WpfControlLibrary1"  
             mc:Ignorable="d"   
             d:DesignHeight="450" d:DesignWidth="800">  
    <Grid.Resources>  
      <CollectionViewSource x:Key="ItemsCVS" Source="{Binding MasterList}" />  
    </Grid.Resources>  
    <DataGrid ItemsSource="{Binding View}" AutoGenerateColumns="False">  
      <DataGrid.Columns>  
        <DataGridTextColumn Header="ID" Binding="{Binding ID}"/>  
        <DataGridTextColumn Header="Info" Binding="{Binding Info}"/>  
        <DataGridComboBoxColumn Header="Lookup"  
                                ItemsSource="{Binding Source={StaticResource ItemsCVS}}"  
                                DisplayMemberPath="MasterInfo"  
                                SelectedValuePath="ID"  
                                SelectedValueBinding="{Binding FKMaster}"/>  
      </DataGrid.Columns>  
    </DataGrid>  
  </Grid>  
</UserControl>  

Result:

I am using comments because I am getting "Access Denied" when trying to answer

Hi Peter thanks for the response. I gave referencing the collectionview as the grids resource but still no joy.

. Sorry I didnt load up any code as to give you better information. The combobox Im trying to wire up is actually
in an ItemControl thats inside a contentcontrol which is a Popup panel inside the Grid of a UserControl. So Ive put the xaml below now for better explanation.

I still a bit suspicious of my attempts though because the combobox I would describe as "empty" as you just see the std empty cell and empty dropdown list. Other times I can see a drop down list that has something in it but text is not visible however in this case nothing.

I am using comments because get "Access Denied" when trying to post answer

Hi Peter thanks for the response. I gave referencing the collectionview as the grids resource but still no joy.

. Sorry I didnt load up any code as to give you better information. The combobox Im trying to wire up is actually
in an ItemControl thats inside a contentcontrol which is a Popup panel inside the Grid of a UserControl. So Ive put the xaml below now for better explanation (sorry cant isnert into comments).

I still a bit suspicious of my attempts though because the combobox I would describe as "empty" as you just see the std empty cell and empty dropdown list. Other times I can see a drop down list that has something in it but text is not visible however in this case nothing.

<UserControl x:Name="TypeUserControl" x:Class="LBAContractRegister.Views.Lists.TypeListView"
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"
mc:Ignorable="d"
xmlns:local="clr-namespace:LBAContractRegister.Views.Lists"
xmlns:cnvtr="clr-namespace:LBAContractRegister.Converters"
xmlns:helpers="clr-namespace:LBAContractRegister.Helpers"
d:DesignHeight="180" d:DesignWidth="660">

<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Types}" HeadersVisibility="Column" SelectedItem="{Binding SelectedType,Converter={cnvtr:DataGridItemConverter}, Mode=TwoWay}" SelectionMode="Single" CanUserAddRows="False" CanUserDeleteRows="False" Background="Transparent"> <DataGrid.Columns> <DataGridTemplateColumn Header="Contract Type Description"> <DataGridTemplateColumn.CellTemplate > <DataTemplate >

<TextBox x:Name="FocusTextBox" Text="{Binding TheEntity.Type1, Mode=TwoWay}" Width="460" IsReadOnly="True" BorderBrush="Transparent"
Style="{StaticResource TextboxStyle}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Retention Type">
<DataGridTemplateColumn.CellTemplate >
<DataTemplate >

<TextBox Text="{Binding TheEntity.IDRetention, Mode=TwoWay}" Width="60" IsReadOnly="True" BorderBrush="Transparent"
Style="{StaticResource TextboxStyle}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>

</DataGrid>
<ContentControl Template="{StaticResource EditPopUp}" Margin="0,0,0,0" >
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl Width="300" >
<helpers:EditRow LabelFor="Contract Type:" >
<TextBox Text="{Binding EditVM.TheEntity.Type1

UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
Mode=TwoWay}" Style="{StaticResource TextboxStyle}"/>
</helpers:EditRow>
<helpers:EditRow LabelFor="Retention ID:" >

UpdateSourceTrigger=PropertyChanged,
NotifyOnSourceUpdated=True,
NotifyOnValidationError=True,
Mode=TwoWay}" Style="{StaticResource TextboxStyle}"/>
</helpers:EditRow>
<helpers:EditRow LabelFor="Retention ID:" >

<ComboBox ItemsSource="{Binding Retentions, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type local:UserControl}}}"
DisplayMemberPath="RecordType" SelectedValue="{Binding EditVM.TheEntity.IDRetention}" SelectedValuePath="IDRetention"/>
</helpers:EditRow>
</ItemsControl>
</ScrollViewer>
</ContentControl>

<TextBlock Text="{Binding ErrorMessage}" HorizontalAlignment="Right" VerticalAlignment="Center"/>
<helpers:Throbber x:Name="Throbber" Visibility="{Binding ThrobberVisible}"/>
</Grid>
</UserControl>

Hi Scott,
I cannot reproduce your code because I don't know yours additional classes.

I reduce your code and this code works fine:

XAML MainWindow:

<Window x:Class="WpfApp1.Window90"  
        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:local="clr-namespace:WpfApp90"  
        xmlns:uc="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"  
        mc:Ignorable="d"  
        Title="Window90" Height="450" Width="800">  
  <Window.DataContext>  
    <local:TypeListViewModel/>  
  </Window.DataContext>  
    <uc:Window90UC1/>  
  </Grid>  
</Window>  

XAML UserControl:

<UserControl x:Class="WpfControlLibrary1.Window90UC1"  
             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:WpfControlLibrary1"  
             mc:Ignorable="d"   
             d:DesignHeight="180" d:DesignWidth="660">  
    <Grid.RowDefinitions>  
      <RowDefinition Height="auto"/>  
      <RowDefinition/>  
    </Grid.RowDefinitions>  
    <DataGrid Grid.Row="1"  
              AutoGenerateColumns="False"  
              ItemsSource="{Binding Types}"  
              HeadersVisibility="Column"  
              SelectedItem="{Binding SelectedType, Mode=TwoWay}"  
              SelectionMode="Single"  
              CanUserAddRows="False"  
              CanUserDeleteRows="False"  
              Background="Transparent">  
      <DataGrid.Columns>  
        <DataGridTemplateColumn Header="Contract Type Description">  
          <DataGridTemplateColumn.CellTemplate >  
            <DataTemplate >  
              <TextBox x:Name="FocusTextBox"   
                       Text="{Binding TheEntity.Type1, Mode=TwoWay}"   
                       Width="460"   
                       IsReadOnly="True"   
                       BorderBrush="Transparent"/>  
            </DataTemplate>  
          </DataGridTemplateColumn.CellTemplate>  
        </DataGridTemplateColumn>  
        <DataGridTemplateColumn Header="Retention Type">  
          <DataGridTemplateColumn.CellTemplate >  
            <DataTemplate >  
              <TextBox Text="{Binding TheEntity.IDRetention, Mode=TwoWay}"   
                       Width="60"   
                       IsReadOnly="True"   
                       BorderBrush="Transparent"/>  
            </DataTemplate>  
          </DataGridTemplateColumn.CellTemplate>  
        </DataGridTemplateColumn>  
      </DataGrid.Columns>  
    </DataGrid>  
    <ContentControl Margin="0,0,0,0" >  
      <ScrollViewer VerticalScrollBarVisibility="Auto">  
        <ItemsControl Width="300" >  
          <ComboBox ItemsSource="{Binding Retentions}"/>  
        </ItemsControl>  
      </ScrollViewer>  
    </ContentControl>  
    <TextBlock Text="{Binding ErrorMessage}"    
               HorizontalAlignment="Right"   
               VerticalAlignment="Center"/>  
  </Grid>  
</UserControl>  

And classes:

using System.Collections.Generic;  
using System.Collections.ObjectModel;  
using System.ComponentModel;  
using System.Runtime.CompilerServices;  
using System.Windows;  
namespace WpfApp90  
  public class TypeListViewModel : CrudVMBase  
    public TypeListViewModel()  
    : base()  
      Types = new ObservableCollection<TypeVM>();  
      for (int i = 1; i < 50; i++)  
        Types.Add(new TypeVM() { TheEntity = new Ent() { Type1 = $"Type {i}", IDRetention = $"IDRet {i}" } });  
      Retentions = new List<string>();  
      for (int i = 1; i < 10; i++) Retentions.Add($"Ret {i}");  
    public ObservableCollection<TypeVM> Types { get; set; }  
    public object SelectedType { get; set; }  
    public string ErrorMessage { get; set; } = "no error";  
    public List<string> Retentions { get; set; }  
  public class CrudVMBase : INotifyPropertyChanged  
    public event PropertyChangedEventHandler PropertyChanged;  
    internal void OnPropertyChanged([CallerMemberName] string propName = "") =>  
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));  
  public class TypeVM  
    public Ent TheEntity { get; set; }  
  public class Ent  
    public string Type1 { get; set; }  
    public string IDRetention { get; set; }  

Result: