How to dynamically generate MenuItems using x:Reference? - c#

I have a MenuItem whose ItemsSource is set to the following CompositeCollection:
<CompositeCollection>
<MenuItem x:Name="SpinnerMenuItem" Header="Waiting..."/>
<CollectionContainer
Collection="{Binding DataContext.Source,
Source={x:Reference SpinnerMenuItem},
Converter={StaticResource NoOpConverter}}"/>
</CompositeCollection>
The breakpoint inside my NoOpConverter is telling me that my collection is successfully getting bound to the CollectionContainer. The problem is, the menu is showing up completely empty! All I get is a popup about 3 pixels high and 10 pixels wide.
Why are my menu items not being displayed? Even the "SpinnerMenuItem" disappears once the bound list is populated. I was not having this issue in the simpler case, when I was just binding to a CollectionViewSource static resource.

This appears to be an issue with CompositeCollection. The workaround is to use a StaticResource instead of a Binding or similar. More info here: Why is CompositeCollection not Freezable?

Related

Wpf ComboboxEdit from binding global list

<dxg:GridColumn.EditTemplate>
<ControlTemplate>
<dxe:ComboBoxEdit
HorizontalContentAlignment="Left"
ItemsSource="{Binding HizmetSaglayiciList}"
SelectedItem="{Binding Hiz_Sag_Id, Mode=TwoWay}"
ValueMember="Hiz_Sag_Id"
IsTextEditable="False"
AllowNullInput="False"
AutoComplete="False"
ImmediatePopup="False"
EditMode="InplaceActive"/>
</ControlTemplate>
</dxg:GridColumn.EditTemplate>
I have global list called HizmetSaglayiciList, but
The combobox does not open when I press the edit button.
I am writing missing any place.
I think this is one of those cases in which the DataContext is not accessible as certain elements (in this case dxg:GridColumn) are not part of visual or logical tree. A solution may be using a Freezable class. Check this Link.
DataContext for the ComboBoxEdit is not the same as for the GridControl, that's why ItemSource binding fails. Assuming your GridControl has a name (let's say it's x:Name="gridTest"), you can simply do the following:
ItemsSource="{Binding DataContext.HizmetSaglayiciList, ElementName=gridTest}"
Actually, you can bind ItemSource to any named element's DataContext.

WPF: "Cyclic reference found while evaluating the Style property on element 'ComboBox'" when changing ResourceDictionary

I have been debugging this case like crazy for the past day and I am yet to find a way to fix it without majorly changing the implementation of a custom control. So here is some back story:
I have a ComboBox and I am changing its Template in order to add a ListBox control in its drop down. For this purpose I have added a the ListBox control to the ComboBox's ControlTemplate after which I have bound the ComboBox and ListBox control's SelectedItem and SelectedIndex via a Twoway binding sometime like this:
SelectedItem="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
SelectedIndex="{Binding SelectedIndex, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
All works perfect. But since I want to support themes of this control via changing the merged ResourceDictionary I hit this nasty issue that for the life of me I cannot get to the bottom of. If I execute a logic that clears the MergedDictionaries and adds new once in the SelectionChanged event of this ComboBox the "Cyclic reference found while evaluating the Style of a ComboBox" is thrown.
Am I missing something obvious?

WPF: Which solution? TabControl with close button and new tab button

I'm trying to find the best solution for a TabControl that both support a close button on each TabItem, and always show a "new tab button" as the last tab.
I've found some half working solutions, but i think that was for MVVM, that I'm not using. Enough to try to understand WPF =)
This is the best solution I've found so far:
http://www.codeproject.com/Articles/493538/Add-Remove-Tabs-Dynamically-in-WPF
A solution that i actually understand. But the problem is that it is using the ItemsSource, and i don't want that. I want to bind the ItemsSource to my own collection without having to have special things in that collection to handle the new tab button.
I've been search for days now but cant find a good solution.
And I'm really new to WPF, otherwise i could probably have adapted the half done solutions I've found, or make them complete. But unfortunately that is way out of my league for now.
Any help appreciated.
I have an open source library which supports MVVM and allows extra content, such as a button to be added into the tab strip. It is sports Chrome style tabs which can tear off.
http://dragablz.net
This is bit of a dirty way to achieve the Add (+) button placed next to the last TabItem without much work. You already know how to place a Delete button next to the TabItem caption so I've not included that logic here.
Basically the logic in this solution is
To bind ItemsSource property to your own collection as well as
the Add TabItem using a CompositeCollection.
Disable selection of
the Add(+) TabItem and instead perform an action to load a new tab when it
is clicked/selected.
XAML bit
<TextBlock x:Name="HiddenItemWithDataContext" Visibility="Collapsed" />
<TabControl x:Name="Tab1" SelectionChanged="Tab1_SelectionChanged" >
<TabControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding DataContext.MyList, Source={x:Reference HiddenItemWithDataContext}}" />
<TabItem Height="0" Width="0" />
<TabItem Header="+" x:Name="AddTabButton"/>
</CompositeCollection>
</TabControl.ItemsSource>
</TabControl>
The code behind
private void Tab1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Contains(AddTabButton))
{
//Logic for adding a new item to the bound collection goes here.
string newItem = "Item " + (MyList.Count + 1);
MyList.Add(newItem);
e.Handled = true;
Dispatcher.BeginInvoke(new Action(() => Tab1.SelectedItem = newItem));
}
}
You could make a converter which appends the Add tab. This way the collection of tabs in you viewmodel will only contain the real tabs.
The problem is then how to know when the Add tab is selected. You could make a TabItem behavior which executes a command when the tab is selected. Incidentally I recommended this for another question just recently, so you can take the code from there: TabItem selected behavior
While I don't actually have the coded solution, I can give some insight on what is most likely the appropriate way to handle this in a WPF/MVVM pattern.
Firstly, if we break down the request it is as follows:
You have a sequence of elements that you want to display.
You want the user to be able to remove an individual element from the sequence.
You want the user to be able to add a new element to the sequence.
Additionally, since you are attempting to use a TabControl, you are also looking to get the behavior that a Selector control provides (element selection), as well as an area to display the element (content) which is selected.
So, if we stick to these behaviors you'll be fine, since the user interface controls can be customized in terms of look and feel.
Of course, the best control for this is the TabControl, which are you already trying to use. If we use this control, it satisfies the first item.
<TabControl ItemsSource="{Binding Path=Customers}" />
Afterwards, you can customize each element, in your case you want to add a Button to each element which will execute a command to remove that element from the sequence. This will satisfy the second item.
<TabControl ...>
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=CustomerId}" />
<Button Command="{Binding Path=RemoveItemCommand, Mode=OneTime,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TabControl}}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
<TabControl.ItemTemplate>
</TabControl>
The last part is a bit more difficult, and will require you to actually have to create a custom control that inherits from the TabControl class, add an ICommand DependencyProperty, and customize the control template so that it not only displays the TabPanel, but right next to it also displays a Button which handles the DependencyProperty you just created (the look and feel of the button will have to be customized as well). Doing all of this will allow you to display your own version of a TabControl which has a faux TabItem, which of course is your "Add" button. This is far far far easier said than done, and I wish you luck. Just remember that the TabPanel wraps onto multiple rows and can go both horizontally or vertically. Basically, this last part is not easy at all.

CompositeCollection doesn't propagate changes in underlying collections

I have what I thought was an ideal scenario for a CompositeCollection, except it seems that changes to the items of the underlying collection do not appear in the CompositeCollection (or, at any rate, not in the control whose source is this CompositeCollection).
EDIT 1: both underlying collections are ObservableCollections.
EDIT 2: the new/updated item gets added, but the contents of that item are not reflected in the drop-down area of the combobox. Each item implements INotifyPropertyChanged.
Am I doing something wrong or is this not supported?
Here's what I have:
<ComboBox SelectedItem="{Binding Products}">
<ComboBox.Resources>
<CollectionViewSource x:Key="CustomProductsSource" Source="{Binding CustomProducts}" />
</ComboBox.Resources>
<ComboBox.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={x:Static local:Products.Standard}}" />
<Separator/>
<CollectionContainer Collection="{Binding Source={StaticResource CustomProductsSource}}"/>
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
What type of collections are Products.Standard and CustomProducts? If they do not have change notification (do not implement INotifyCollectionChanged), then CompositeCollection has no means to know that something changed.
ObservableCollection<> for example should work just fine in this scenario.
I fixed it - the problem was (as it is most often is) an idiot programmer (that would be me).
It so happened that Item of the collection (individual Product class) had an overriden ToString() method returned the Name property of the Product instance. So, the combo box was showing what it was supposed to show, which is the name of the product, except it was doing it based on the ToString() method... BECAUSE I FORGOT TO CREATE A DATATEMPLATE for the ComboBox. No wonder then that changes to the Name were not reflected

Visible name in XAML

I have a datagrid with a column containing a ComboBox. I've set the Name for my combobox, but this name is not visible in code, why?
<DataGrid ...>
<DataGrid.Columns>
<DataGrid.TemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="mex" Style="{DynamicResource ComboBoxStyle}"
ItemsSource="{Binding Path=combolist}"
SelectionChanged="status_SelectionChanged" Height="auto" Width="Auto">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGrid.TemplateColumn>
...
</DataGrid.Columns>
</DataGrid>
In C# code mex is empty, not visible, why?
I tried x:Name="mex" as well, but it is still not visible.
c#:
mex.ItemsSource = dt;
undifined mex
A DataGridColumn is never actually in the Logical or Visual Tree; it is always a DataGridRow with DataGridCells, since they are automatically created for each row in the DataGrid.
The only way to reach your component is to build a complex Binding or find it using a Logical or Visual Tree helper.
BTW you should set your ItemsSource of your ComboBox through Bindings to available data from your row. You can't create bindings using ElementName within a DataGridTemplateColumn as again it is not in the Logical or Visual Tree.
I found an interesting link that explains the Visual Tree of a DataGrid: http://blogs.msdn.com/b/vinsibal/archive/2008/08/14/wpf-datagrid-dissecting-the-visual-layout.aspx.
You cannot directly reference elements when they are stored within an items template. Normally you'd want to allow the viewmodel to handle the bindings by setting the datacontext to the parent object and then allow the elements within the items template to pick this up. However based upon your question it looks as if you are attempting to do this directly from code behind.
Here are two similar questions and solutions for setting up the datacontext of an item as well as referencing an element within an items template. Hope this helps
Access parent DataContext from DataTemplate
and
WPF - ItemsControl - How do I get find my "CheckBox" item that is in the ItemTemplate?
because template is used by all rows.if name is usable .must have the same name in visual tree at one time.

Categories

Resources