Checked Combobox inside DataTemplate which is inside Listbox DataTemplate - wpf

I have a listbox and its data template. But in its DataTemplate i have CheckedCombobox DataTemplate. Wanted to know how to set the bindings. I have tried below things to get which are items checked in child element by each listbox item. Below is the code which is not working.
<ListBox
Name="ListLayers"
ItemsSource="{Binding LstDragList}"
Height="123"
Width="283"
SelectedIndex="{Binding SelectedRow, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<ComboBox
Name="childlayers"
Style="{StaticResource EditableCboStyle}"
ItemsSource="{Binding lstLayerModel}"
Text="{Binding SelectedLayers, UpdateSourceTrigger=PropertyChanged}"
ItemContainerStyle="{DynamicResource ComboboxItemContainerStyle}"
Width="200"
IsEditable="True"
IsReadOnly="False"
PreviewMouseLeftButtonDown="ComboBox_PreviewMouseLeftButtonDown"
>
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:Model}">
<CheckBox
VerticalAlignment="Center"
VerticalContentAlignment="Center"
IsChecked="{Binding IsChecked}"
Content="{Binding DisplayLayer}"
/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Models:
public class DragList : ObservableObject
{
public DragList()
{
_selectedLayers = string.Empty;
}
public string FileName { get; set; }
public ObservableCollection<Model> lstLayerModel { get; set; }
public string Text { get; set; }
private string _selectedLayers;
public string SelectedLayers
{
get { return _selectedLayers; }
set { SetAndNotify(ref _selectedLayers, value, () => this.SelectedLayers); }
}
private int _selectedLayerInx;
public int SelectedLayerInx
{
get { return _selectedLayerInx; }
set { SetAndNotify(ref _selectedLayerInx, value, () => this.SelectedLayerInx); }
}
}
my constructor viewmodel:
public ViewModel()
{
_lstLayers = new ObservableCollection<Model>();
mCheckedItems = new ObservableCollection<Model>();
_tempDragList = new List<DragList>();
_lstLayers.CollectionChanged += _lstLayers_CollectionChanged;
_lstLayers.Add(new Model
{
LayerName = "All",
LayerNumber = "",
IsChecked = true
});
_lstLayers.Add(new Model { LayerName = "Layer one", LayerNumber = "1", IsChecked = false });
_lstLayers.Add(new Model { LayerName = "Layer two", LayerNumber = "2", IsChecked = false });
_lstLayers.Add(new Model { LayerName = "Layer three", LayerNumber = "3", IsChecked = false });
_lstDragList = new List<DragList>();
_lstDragList.Add(new DragList { FileName = "Test", lstLayerModel = _lstLayers });
_lstDragList.Add(new DragList { FileName = "Test1", lstLayerModel = _lstLayers });
_tempDragList = _lstDragList;
}

It's because you need to use RelativeSource on the inner bindings to get the correct DataContext like so:
{Binding DataContext.SelectedRow, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}
There are many other articles on RelativeSource here on SO that should provide you with that you need.

Related

Unselect WPF DataGrid when ItemsSource changed

I have the following DataGrid in WPF :
<DataGrid x:Name="dgPatientMedicationOrderList" Width="Auto" HorizontalAlignment="Stretch" RowHeight="40" Background="Transparent" HorizontalContentAlignment="Left"
GridLinesVisibility="None" RowHeaderWidth="0" VirtualizingStackPanel.VirtualizationMode="Standard" SelectedIndex="-1"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" AutoGenerateColumns="False" SelectionMode="Single"
IsSynchronizedWithCurrentItem="True" RowDetailsVisibilityMode="VisibleWhenSelected" VerticalAlignment="Stretch" IsReadOnly="True"
ItemsSource="{Binding PatientOrdersCollectionView}">
When a user clicks on a row in the DataGrid, it's SelectionChanged event is fired and the bound ViewModel command drives the View to load another user control corresponding to the DG row. In my view I am changing my datagrid's source binding from my ViewModel. Now the problem is that every time the ItemsSource is changed the SelectionChanged event is fired selecting the first item in the DataGrid; this is followed by the view loading the user control without the user explicitly selecting the DataGrid row. How can I prevent the DataGrid from selecting any Row when it's ItemsSource is changed ?
Simplified Demo Code:
XAML:
<Button Content="Change Source" Command="{Binding ChangeItemsSourceCmd}" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,20" />
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Orientation="Vertical">
<TextBlock Text="{Binding SelectedPerson.Id, StringFormat=ID: {0}}" />
<TextBlock Text="{Binding SelectedPerson.Name, StringFormat=Name: {0}}" />
<TextBlock Text="{Binding SelectedPerson.Gender, StringFormat=Gender: {0}}" />
<TextBlock Text="{Binding SelectedPerson.Country, StringFormat=Country: {0}}" />
</StackPanel>
</Grid>
ViewModel:
public class WindowViewModel : ViewModelBase
{
private Person _selectedPerson;
private ObservableCollection<Person> _personList;
public Person SelectedPerson
{
get { return _selectedPerson; }
set { RaisePropertyChange<Person>(() => SelectedPerson, ref _selectedPerson, ref value); }
}
public ObservableCollection<Person> PersonList
{
get { return _personList; }
set
{
SelectedPerson = null;
RaisePropertyChange<ObservableCollection<Person>>(() => PersonList, ref _personList, ref value);
}
}
public WindowViewModel()
{
PersonList = new ObservableCollection<Person>()
{
new Person() { Id=101, Name="Mahesh", Gender="Male", Country="India"},
new Person() { Id=102, Name="Srinivas", Gender="Male", Country="Sri Lanka"},
new Person() { Id=103, Name="Isha", Gender="Female", Country="United States"},
new Person() { Id=104, Name="Salim", Gender="Male", Country="Pakistan"}
};
}
public ICommand ChangeItemsSourceCmd
{
get
{
return new RelayCommand(ChangeItemsSourceCmdHandler);
}
}
private void ChangeItemsSourceCmdHandler()
{
PersonList = new ObservableCollection<Person>()
{
new Person() { Id=105, Name="Raman", Gender="Male", Country="Uganda"},
new Person() { Id=106, Name="Anurag", Gender="Male", Country="England"},
new Person() { Id=107, Name="Komal", Gender="Female", Country="Thailand"},
new Person() { Id=108, Name="Nitin", Gender="Male", Country="Africa"}
};
}
}
You should:
1.Add a SelectedItem Binding in your DataGrid:
SelectedItem="{Binding Selected, Mode=TwoWay}"
2.Have the related property (firing PropertyChanged of course)
public object Selected
{
get { return selected; }
set
{
selected = value;
OnPropertyChanged("Selected");
}
}
3.Set it to null in your Itemssource setter (or before you change it)
public IEnumerable PatientOrdersCollectionView
{
get { return patientOrdersCollectionView; }
set
{
Selected = null; // Put it to null here to unselect it from grid
patientOrdersCollectionView = value;
OnPropertyChanged("PatientOrdersCollectionView");
}
}
Should do the trick.

Binding a Combobox within a DataTemplate correctly in Wpf

I am trying to bind the ComboBox below to the list of Characters in the ObservableCollection, but it wont show anything. Any ideas why?
XAML:
<TabControl ItemsSource ="{Binding TextEditors}"
<TabControl.ContentTemplate>
<DataTemplate>
<ListBox> ItemsSource="{Binding TextLines}"
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<ComboBox
ItemsSource="{Binding DataContext.InvCharacter, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}"
DisplayMemberPath="name"
SelectedValuePath="cid"
SelectedValue="{Binding cid}">
</ComboBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
this is the class i am referring to:
class TextEditorVM: IViewModel {
public ObservableCollection<TextLineVM> TextLines { get { return textLines; } set { textLines = value;} }
public ObservableCollection<T_Character> InvCharacters { get { return invCharacters; } set { invCharacters = value; } }
public TextEditorVM(T_Dialogue dialogue)
{
DialogueManager.Instance.Register(this);
this.TextLines = new ObservableCollection<TextLineVM>();
this.InvCharacters = new ObservableCollection<T_Character>();
}
}
and the MainVM:
class MainVM : IViewModel
{
public ObservableCollection<TextEditorVM> TextEditors { get { return textEditors; } set { textEditors = value; OnPropertyChanged("TextEditors"); }
}
my T_Character Class looks like this now :
public class T_Character
{
public String cid { get; set; }
public String name { get; set; }
public T_Character(String cid, String name)
{
this.cid = cid;
this.name = name;
}
}
The DataContext of the TabControl is of type MainVM. The RelativeSource of the ComboBox binding should not be the TabControl but rather the ListBox.
Your InvCharacters property is on your TextEditorVM object which is in your ObservableCollection, however your binding is referencing TabControl.DataContext, which is MainVM, and does not contain that property.
Switch your RelativeSource binding to reference TabItem (it gets created automatically when you bind TabControl.ItemsSource) or ListBox to reference your TextEditorVM object
<ComboBox ItemsSource="{Binding DataContext.InvCharacters,
RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}">
</ComboBox>

DataGrid won't update ViewModel

I've got a datagrid which is bound to an ObservableCollection of the ViewModel below. The Datagrid will display all values correctly, so the binding seems to be working, but if I change some value the Grid won't call the setter's of my VM. Could somebody tell me why?
Here's my ViewModel:
public class DocumentVm : ViewModelBase
{
private Document document;
public bool IsNew { get; private set; }
public Document Document {
get { return document; }
}
public DocumentVm(Document document)
{
this.document = document;
IsNew = false;
}
public DocumentVm(Document document, bool isNew)
{
this.document = document;
IsNew = isNew;
}
public String Name
{
get { return document.Name; }
set { document.Name = value; RaisePropertyChangedEvent("Name");}
}
public String Path
{
get { return document.Path; }
set { document.Path = value; }
}
public String Metadata
{
get { return document.Metadata; }
set { document.Metadata = value; }
}
public int SpeechId
{
get { return document.SpeechId; }
set { document.SpeechId = value; }
}
}
Here is the XAML Code:
<DataGrid Margin="3" Grid.Row="7" Grid.Column="1" BorderThickness="0"
ItemsSource="{Binding Path=CurrentSpeech.Documents, Mode=TwoWay}"
SelectedItem="{Binding Path=CurrentSpeech.CurrentDocument, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Name, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="MetaDaten" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Metadata, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Pfad" Width="SizeToCells">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
Thank you all!
UpdateSourceTrigger=PropertyChanged on the Bindings was missing.
Not sure why the setters aren't getting called but if you use dependency properties they actually do get called. I'm far too drunk to investigate why the CLR properties aren't getting set in your project but this works for me.
public partial class MainWindow : Window
{ public ObservableCollection<DocumentVm> Items
{
get { return (ObservableCollection<DocumentVm>)GetValue(ItemsProperty); }
set { SetValue(ItemsProperty, value); }
}
public static readonly DependencyProperty ItemsProperty =
DependencyProperty.Register("Items", typeof(ObservableCollection<DocumentVm>), typeof(MainWindow), new PropertyMetadata(null));
public MainWindow()
{
InitializeComponent();
Items = new ObservableCollection<DocumentVm>();
Items.Add(new DocumentVm() { Name = "Name 1"});
Items.Add(new DocumentVm() { Name = "Name 2"});
}
}
public class DocumentVm : DependencyObject
{
public string Name
{
get { return (string)GetValue(NameProperty); }
set { SetValue(NameProperty, value); }
}
public static readonly DependencyProperty NameProperty =
DependencyProperty.Register("Name", typeof(string), typeof(DocumentVm), new PropertyMetadata(null, new PropertyChangedCallback( (s, e)=>
{
// This will run when the property is set.
})));
}

Failing to add items in listbox on button Click

I am a newbie to MVVM. I have two grid's inside by main window where one grid contains a listbox towards left side and other grid on the right side contains list view and 2 buttons.
I am able to add items to listview and even figure out how to get the selected item from list view. Once I select the item on list view and click a button(Connect), listbox towards left shud get updated with items which I have added from viewmodel class.
View below shows Listbox towards my left:
<Grid Grid.Column="0" Name="BoardTabSelect" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<ListBox Name="ButtonPanel"
ItemsSource="{Binding BoardTabs, Mode=TwoWay}"
SelectedItem="{Binding SelectedTab, Mode=TwoWay}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="BoardtabChanger"
Margin="53,27,0,0"
Text="{Binding TabOperation}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
View below shows listview towards Right along with 2 Buttons:
<Grid Grid.Row="0" Name="MainConnectGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" >
<ListView Name="listView"
ItemsSource="{Binding Products}"
SelectedItem="{Binding SelectedProduct, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Width="300"
Header="Name"
DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Width="283"
Header="Connection Status"
DisplayMemberBinding="{Binding Connection_Status}" />
</GridView>
</ListView.View>
</ListView>
<Button Content="Connect"
Height="23" Width="100"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="55,519,0,0"
Name="ConnectBtnGrid"
Command="{Binding Path=ConnectCommand}" />
<Button Content="Disconnect"
Height="23" Width="100"
HorizontalAlignment="Left" VerticalAlignment="Top"
Margin="446,519,0,0"
Name="DisconnectBtn"
Command="{Binding Path=DisconnectCommand}" />
</Grid>
VIEW MODEL:
public class ProductViewModel : INotifyPropertyChanged
{
public List<Product> m_Products;
public List<Product> m_BoardTabs;
public ProductViewModel()
{
m_Products = new List<Product>()
{
new Product() {Name = "Bavaria", Connection_Status = "Disconnected"},
new Product() {Name = "Redhook", Connection_Status = "Disconnected"},
};
m_BoardTabs = new List<Product>()
{
new Product() {TabOperation = "Connect"}
};
}
public List<Product> Products { get; set; }
public List<Product> BoardTabs { get; set; }
private Product m_SelectedItem;
public Product SelectedProduct
{
get { return m_SelectedItem; }
set
{
m_SelectedItem = value;
NotifyPropertyChanged("SelectedProduct");
}
}
private Product m_SelectedTab;
public Product SelectedTab
{
get { return m_SelectedTab; }
set
{
m_SelectedTab = value;
NotifyPropertyChanged("SelectedTab");
}
}
private ICommand mUpdater;
public ICommand ConnectCommand
{
get
{
if (mUpdater == null)
mUpdater = new DelegateCommand(new Action(SaveExecuted), new Func<bool>(SaveCanExecute));
return mUpdater;
}
set { mUpdater = value; }
}
public bool SaveCanExecute()
{
return true;
}
public void SaveExecuted()
{
if (SelectedProduct.Connection_Status == "Disconnected" && SelectedProduct.Name == "Bavaria")
{
SelectedProduct.Connection_Status = "Connected";
m_BoardTabs.Add(new Product() { TabOperation = "I2C" });
m_BoardTabs.Add(new Product() { TabOperation = "Voltage" });
m_BoardTabs.Add(new Product() { TabOperation = "Codec" });
}
}
}
Inside the Save Executed method I am trying to add the items in listbox but when I run the application and select item from listview and press CONNECT Btn, the list does not get updated. My model class is complete with all three properties (Name, Connection Status and TabOperation)
BoardTabs and Products need to be an ObservableCollection<Product>. Otherwise WPF doesn't get informed about new items in the lists and thus can't update the UI.
Check properies of ProductViewModel. It implements INotifyPropertyChanged , and in Products, BoardTabs, you not notify the change .
public List<Product> Products
{
get
{
return m_Products;
}
set
{
m_Products = value;
NotifyPropertyChanged("Products")
}
}
public List<Product> BoardTabs
{
get
{
return m_BoardTabs;
}
set
{
m_BoardTabs = value;
NotifyPropertyChanged("BoardTabs")
}
}

WPF Datagrid hierarchical data recorded as strings

I have information that was gathered from a service about TFS builds put into ViewModels.
Here are the models:
public class Collection : ViewModel
{
private string _name = string.Empty;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged(() => Name);
}
}
private ObservableCollection<Project> _projects = new ObservableCollection<TFSProject>();
public ObservableCollection<Project> Projects
{
get { return _projects; }
set
{
_projects = value;
OnPropertyChanged(() => Projects);
}
}
}
public class Project : ViewModel
{
private string _name = string.Empty;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged(() => Name);
}
}
private ObservableCollection<string> _buildDefinitions = new ObservableCollection<string>();
public ObservableCollection<string> BuildDefinitions
{
get { return _buildDefinitions; }
set
{
_buildDefinitions = value;
OnPropertyChanged(() => BuildDefinitions);
}
}
}
I am binding my combobox's itemssource to a ObservableCollection<Collection>. The problem is that the collection, project, and build definition names are stored in a class that defines them as separate string properties:
public class BuildMonitor : INotifyPropertyChanged
{
[Description("In TFS, each team project exists within a TFS Collection. This is the name of the collection applicable to the build that this monitor is for. Default='Vision2010'")]
public string Collection
{
get { return collection_; }
set
{
collection_ = value;
OnPropertyChanged(() => Collection);
}
}
private string collection_ = "Vision2010";
[Description("BuildDefintions reside within a TeamProject. This is the name of the TeamProject where the build definition this monitor is for resides. Default='Double-Take2010'")]
public string TeamProject { get { return teamProject_; } set { teamProject_ = value; OnPropertyChanged(() => TeamProject); } }
private string teamProject_ = "Double-Take2010";
[Description("Builds are defined in TFS as the execution of a particular BuildDefinition. This is the name of the build defintion (thus; the build) this monitor is for.")]
public string BuildDefinition { get { return buildDefinition_; } set { buildDefinition_ = value; OnPropertyChanged(() => BuildDefinition); } }
private string buildDefinition_;
[Description("Used only if this monitor should watch for builds specified by a particular user. Enter the domain name of the user, or leave blank to monitor builds by any user.")]
public string RequestedByFilter { get { return requestedByFilter_; } set { requestedByFilter_ = value; OnPropertyChanged(() => RequestedByFilter); } }
private string requestedByFilter_;
[Description("The command to execute when the build monitor is triggered.")]
public string Command { get { return command_; } set { command_ = value; OnPropertyChanged(() => Command); } }
private string command_;
[Description("The arguments to pass to the command. Arguments will resolve known build monitor macros.")]
public string Arguments { get { return arguments_; } set { arguments_ = value; OnPropertyChanged(() => Arguments); } }
private string arguments_;
[Description("If TRUE, the monitor will fire only once, at which point it will be marked as 'invalid' and never fire again.")]
public bool RunOnce { get { return runOnce_; } set { runOnce_ = value; OnPropertyChanged(() => RunOnce); } }
private bool runOnce_ = false;
[Description("The maximum age (in hours) a build can be (since finished), for the monitor to consider it for processing. Default='0'")]
public int MaxAgeInHours { get { return maxAgeInHours_; } set { maxAgeInHours_ = value; OnPropertyChanged(() => MaxAgeInHours); } }
private int maxAgeInHours_ = 0;
[Description("Which status trigger the monitor should 'fire' on. When the build status matches this trigger, the monitor command will be executed. Default='Succeeded'")]
public BuildStatus EventTrigger { get { return eventTrigger_; } set { eventTrigger_ = value; OnPropertyChanged(() => EventTrigger); } }
private BuildStatus eventTrigger_ = BuildStatus.Succeeded;
[Browsable(false), Description("Used internally to reliably compare two BuildMonitors against each other.")]
public Guid ID { get { return id_; } set { id_ = value; } }
private Guid id_ = Guid.NewGuid();
[Browsable(false), Description("Used internally to determine if the monitor is still valid/should be processed.")]
public bool IsEnabled { get { return isEnabled_; } set { isEnabled_ = value; } }
private bool isEnabled_ = true;
[Browsable(false), XmlIgnore, Description("Used internally to track when the monitor is 'busy' (currently running the 'Command' selected.")]
public int CurrentProcessID { get { return currentProcessID_; } set { currentProcessID_ = value; } }
private int currentProcessID_ = 0;
[Browsable(false), XmlIgnore, Description("Used internally to track the build that the monitor is currently processing.")]
private string currentBuildUri_;
public string CurrentBuildUri { get { return currentBuildUri_; } set { currentBuildUri_ = value; } }
[field: NonSerialized, Browsable(false)]
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
MemberExpression memberExpression = (MemberExpression)propertyExpression.Body;
string propertyName = memberExpression.Member.Name;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In my xaml I have attempted to represent the selection of this data by setting the itemssource of the collection combobbox to a relative source binding which is the ObservableCollection<Collection>. I get the items in the list ok but since the itemssource is a List<BuildMonitors>, I can't seem to get the selected item to map over the name property of the selected item to the actual binding of the data item (string Collection in the BuildMonitor instance).
<tk:DataGrid ItemsSource="{Binding Monitors}"
AutoGenerateColumns="False">
<tk:DataGrid.Columns>
<tk:DataGridTemplateColumn Header="Collection">
<tk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox x:Name="Collection"
ItemsSource="{Binding Path=AllCollections, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type apollo:BuildMonitorNew}}}"
DisplayMemberPath="Name"
SelectedItem="{Binding .}"
SelectedValuePath="Collection"
SelectedValue="{Binding Name}"/>
</DataTemplate>
</tk:DataGridTemplateColumn.CellEditingTemplate>
<tk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Collection,Mode=TwoWay}"/>
</DataTemplate>
</tk:DataGridTemplateColumn.CellTemplate>
</tk:DataGridTemplateColumn>
<tk:DataGridTemplateColumn Header="Project">
<tk:DataGridTemplateColumn.CellEditingTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding ElementName=Collection,Path=SelectedItem.Projects}">
<ComboBox x:Name="Projects"
ItemsSource="{Binding}"
DisplayMemberPath="Name"/>
</HierarchicalDataTemplate>
</tk:DataGridTemplateColumn.CellEditingTemplate>
</tk:DataGridTemplateColumn>
<tk:DataGridTextColumn Binding="{Binding Command}"
Header="Command"/>
<tk:DataGridTextColumn Binding="{Binding Arguments}"
Header="Arguments"
Width="*"/>
</tk:DataGrid.Columns>
My first thought is that although my viewmodel may be a better representation of the data (hierarchical), the structure of the data to select vs the data to actual store is too different.
I would love to be wrong here and find a snazzy way to convert the data that is actually selected(Collection,Project, and then BuildDefinition) to the path of the data that is stored (BuildMonitor).
Any ideas?
I found that the multivalue converter allowed me to transform the hierarchical structure into parts for the combobox item source where they are representing a list.
Converter:
public class BuildMonitorItemSource : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values.Count() == 2)
{
string collectionToGet = parameter.ToString();
ObservableCollection<TFSCollection> allcollections = values[1] as ObservableCollection<TFSCollection>;
BuildLauncher.BuildMonitor currentBM = (values[0] as BuildLauncher.BuildMonitor);
if (collectionToGet.Equals("Projects"))
{
return allcollections.FirstOrDefault(x => x.Name.Equals(currentBM.Collection, StringComparison.OrdinalIgnoreCase)).Projects;
}
else if (collectionToGet.Equals("BuildDefinitions"))
{
TFSCollection currentCollection = allcollections.FirstOrDefault(x => x.Name.Equals(currentBM.Collection));
TFSProject currentProject = currentCollection.Projects.FirstOrDefault(x => x.Name.Equals(currentBM.TeamProject));
return currentProject.BuildDefinitions;
}
}
return values;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
This is the modified xml:
<tk:DataGrid ItemsSource="{Binding Monitors}"
AutoGenerateColumns="False"
IsEnabled="{Binding RelativeSource={RelativeSource AncestorType=apollo:BuildMonitorNew}, Path=TFSAuthenticated}"
Name="MontiorsGrid">
<tk:DataGrid.Columns>
<tk:DataGridCheckBoxColumn Binding="{Binding IsEnabled}">
<tk:DataGridCheckBoxColumn.Header>
<Ellipse Grid.Column="0"
HorizontalAlignment="Left"
Height="10" Width="10"
Stroke="Black"
StrokeThickness="1"
Fill="Green"/>
</tk:DataGridCheckBoxColumn.Header>
</tk:DataGridCheckBoxColumn>
<tk:DataGridTemplateColumn Header="Collection">
<tk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox x:Name="CollectionCombo"
ItemsSource="{Binding Path=AllCollections, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type apollo:BuildMonitorNew}}}"
DisplayMemberPath="Name"
SelectedValue="{Binding Collection}"
SelectedValuePath="Name">
</ComboBox>
</DataTemplate>
</tk:DataGridTemplateColumn.CellEditingTemplate>
<tk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Collection,Mode=TwoWay}"/>
</DataTemplate>
</tk:DataGridTemplateColumn.CellTemplate>
</tk:DataGridTemplateColumn>
<tk:DataGridTemplateColumn Header="Project">
<tk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox x:Name="Projects"
DisplayMemberPath="Name"
SelectedValue="{Binding TeamProject}"
SelectedValuePath="Name">
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource GetItemSource}" ConverterParameter="Projects" >
<Binding Path="." />
<Binding Path="AllCollections" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type apollo:BuildMonitorNew}}"/>
</MultiBinding>
</ComboBox.ItemsSource>
</ComboBox>
</DataTemplate>
</tk:DataGridTemplateColumn.CellEditingTemplate>
<tk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding TeamProject,Mode=TwoWay}"/>
</DataTemplate>
</tk:DataGridTemplateColumn.CellTemplate>
</tk:DataGridTemplateColumn>
<tk:DataGridTemplateColumn Header="Build Definition">
<tk:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedItem="{Binding BuildDefinition}">
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource GetItemSource}" ConverterParameter="BuildDefinitions">
<Binding Path="." />
<Binding Path="AllCollections" RelativeSource="{RelativeSource Mode=FindAncestor,AncestorType={x:Type apollo:BuildMonitorNew}}"/>
</MultiBinding>
</ComboBox.ItemsSource>
</ComboBox>
</DataTemplate>
</tk:DataGridTemplateColumn.CellEditingTemplate>
<tk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding BuildDefinition,Mode=TwoWay}"/>
</DataTemplate>
</tk:DataGridTemplateColumn.CellTemplate>
</tk:DataGridTemplateColumn>
<tk:DataGridTextColumn Binding="{Binding Command}"
Header="Command"/>
<tk:DataGridTextColumn Binding="{Binding Arguments}"
Header="Arguments"
Width="*"/>
</tk:DataGrid.Columns>
</tk:DataGrid>

Resources