Reputation: 6368
Working Example :
I have a table called groups as shown below :
After looking at the image above I think you might have understood that primary key and foreign key exist in the same table. I think this is what developers call cyclic reference.
In MainWindow.xaml I have a DataGrid which contains three columns namely Group Name, Parent Name, Description. The xaml looks like :
<Window .......>
<Window.DataContext>
<self:MainWindowViewModel />
</Window.DataContext>
<DataGrid ItemsSource="{Binding Groups}" TabIndex="1">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Group Name" Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding GroupName}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding GroupName}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Parent" Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ParentID, Converter={StaticResource GroupIDToGroupNameConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.GroupsCollection, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
SelectedValue="{Binding ParentID}"
SelectedValuePath="GroupID"
DisplayMemberPath="GroupName"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Description" Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Description}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</power:PowerDataGrid.Columns>
</power:PowerDataGrid>
</Window>
Now I have a ViewModel called MainWindowViewModel
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
SampleDBContext sampleDBContext = new SampleDBContext();
Groups = new ObservableCollection<Group>();
GroupsCollection = new ObservableCollection<Group>(from g in sampleDBContext.Groups select g);
}
private ObservableCollection<Group> _groups;
public ObservableCollection<Group> Groups
{
get
{
return _groups;
}
set
{
_groups = value;
OnPropertyChanged("Groups");
}
}
private ObservableCollection<Group> _groupsCollection;
public ObservableCollection<Group> GroupsCollection
{
get
{
return _groupsCollection;
}
set
{
_groupsCollection = value;
OnPropertyChanged("GroupsCollection");
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertryName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertryName));
}
}
#endregion
}
GroupIDToGroupName.cs //Converter
public class GroupIDToGroupName : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null)
{
SampleDBContext sampleDBContext = new SampleDBContext();
return (from g in sampleDBContext.Groups
where g.GroupID == (int)value
select g.GroupName).FirstOrDefault();
}
else
{
return "";
}
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
SampleDBContext sampleDBContext = new SampleDBContext();
return (from g in sampleDBContext.Groups
where g.GroupName == (string)value
select g.GroupID).FirstOrDefault();
}
}
In App.xaml :
<self:GroupIDToGroupName x:Key="GroupIDToGroupNameConveerter" />
My Case (Very similar to above sample):
I just want to use a Multi-Column ComboBox instead of simple ComboBox inside DataGrid.
I have two tables :
Now I have set up my code exactly as the above mentioned code.
I have added an extra class called GroupIDAndNameWithCorrespondingEffect like :
public class GroupIDAndNameWithCorrespondingEffect : INotifyPropertyChanged
{
private int _groupID;
public int GroupID
{
get
{
return _groupID;
}
set
{
_groupID = value;
OnPropertyChanged("GroupID");
}
}
private string _groupName;
public string GroupName
{
get
{
return _groupName;
}
set
{
_groupName = value;
OnPropertyChanged("GroupName");
}
}
private string _correspondingEffect;
public string CorrespondingEffect
{
get
{
return _correspondingEffect;
}
set
{
_correspondingEffect = value;
OnPropertyChanged("CorrespondingEffect");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Changes in my ViewModel :
I removed the property GroupsCollection and all its references and added a new property called GroupIDAndNamesWithCorrespondingEffects as below :
private ObservableCollection<GroupIDAndNameWithCorrespondingEffect> _groupIDAndNamesWithCorrespondingEffects;
public ObservableCollection<GroupIDAndNameWithCorrespondingEffect> GroupIDAndNamesWithCorrespondingEffects
{
get
{
return _groupIDAndNamesWithCorrespondingEffects;
}
set
{
_groupIDAndNamesWithCorrespondingEffects = value;
OnPropertyChanged("GroupIDAndNamesWithCorrespondingEffects");
}
}
And in the Constructor :
List<GroupIDAndNameWithCorrespondingEffect> _GroupIDAndNamesWithCorrespondingEffects = (
from g in sampleDBContext.Groups
select new GroupIDAndNameWithCorrespondingEffect
{
GroupID = g.GroupID,
GroupName = g.GroupName,
CorrespondingEffect = g.Effect.Effect1
}
).ToList();
GroupIDAndNamesWithCorrespondingEffects
= new ObservableCollection<GroupIDAndNameWithCorrespondingEffect>(
_GroupIDAndNamesWithCorrespondingEffects.Where
(
u => !GetAllChildren(25)
.Select(x => x.GroupID)
.Contains(u.GroupID)
).ToList()
);
In my MainWindow.xaml I have added a resource as follows :
<Window.Resources>
<CollectionViewSource x:Key="GroupNamesWithCorrespondingEffectsCollection" Source="{Binding GroupIDAndNamesWithCorrespondingEffects}" />
</Window.Resources>
Inside Grid's Resources :
<Grid.Resources>
<CompositeCollection x:Key="Items">
<ComboBoxItem IsEnabled="False" Background="#FF2A2A2A" Foreground="White">
<Grid TextElement.FontWeight="Bold" >
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A" />
<ColumnDefinition Width="50" />
<ColumnDefinition SharedSizeGroup="B" />
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="Group Name" />
<TextBlock Grid.Column="2" Text="Effect" />
</Grid.Children>
</Grid>
</ComboBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource GroupNamesWithCorrespondingEffectsCollection}}" />
</CompositeCollection>
<DataTemplate DataType="{x:Type self:GroupIDAndNameWithCorrespondingEffect}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A" />
<ColumnDefinition Width="50" />
<ColumnDefinition SharedSizeGroup="B" />
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="{Binding GroupName}" />
<TextBlock Grid.Column="2" Text="{Binding CorrespondingEffect}" />
</Grid.Children>
</Grid>
</DataTemplate>
</Grid.Resources>
And I changed the ItemsSource of ComboBox to ItemsSource="{DynamicResource Items}".
Problems :
When I run the program ComboBox displays all the items correctly. Also, two columns with headers are displayed. Its working fine, but when I press Enter or TAB, then focus remains in the same cell and the comboBox's text displays namespace of GroupIDAndNameWithCorrespondingEffect
Here is the image of Problem :
Sample:
Incase if somebody wants to check the sample then it is available here. And database files are available here.
Upvotes: 0
Views: 1215
Reputation: 6368
Got it!!!!
I declared the resource with key="Items"
in Grid. So, when I Check for ComboBox.SelectedIndex
in PreviewKeyDown
event of DataGrid, it gives me -1
and so my logic works as unexpected. Also, at this time I get ComboBox.Items.Count = 0
.
So I just changed the place of declaration of the resource. I mean I deleted Grid.Resources Section
and in ComboBox.Resources section
I wrote the same code. Now it is working fine. Now in PreviewKeyDown of DataGrid
, I get the expected SelectedIndex of the ComboBox
as well as ComboBox.Items.Count is equal to the Count in Source
.
I don't know why this happens? As I have used it as DynamicResource
I expect it to work even if it is declared in Grid.Resources section.
Upvotes: 1
Reputation: 36
One way to solve this is in http://www.shujaat.net/2010/08/wpf-editable-combobox-with-datatemplate.html
TextSearch.TextPath="GroupName"
Another way would be to provide a DataTemplate to the ComboBox's ItemTemplate
<ComboBox ItemTemplate="{StaticResource myDataTemplate}"/>
Upvotes: 0
Reputation: 120
Maybe ... maybe your combobox just needs IsEditable = false. IsEditable = True causes comboboxes to show namespaces instead of the text.
Upvotes: 0