How to add items to a combobox with IronPython and XAML - xaml

I have the following XAML
<ListView x:Name="listViewTarget" Height="560" Canvas.Left="10" Canvas.Top="101" Width="924" AllowDrop="True">
<ListView.View>
<GridView>
<GridViewColumn Width="350" Header="File or Email">
</GridViewColumn>
<GridViewColumn Width="500" Header="Awaiting Item" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="350" x:Name="cboAI" HorizontalAlignment="Right">
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
The ListView has two columns, in the second column I need a combobox of options.
I have a drag drop event associated with the ListView, when you drag a file onto the list the filenames are dynamically added successfully to the list view.
def listView_drag_drop(sender, e):
data = e.Data.GetData(DataFormats.FileDrop, False)
for s in data:
sender.Items.Add(s)
lstViewDropTarget = LogicalTreeHelper.FindLogicalNode(_tikitSender,listViewTarget')
lstViewDropTarget.Drop += listView_drag_drop
What I need to do inside that event is to also populate the combobox, which is currently empty.
I have tried to work out how to get a reference to the combobox to be able to set its ItemsSource property, but after days of effort I have failed.
I am new to IronPython and appreciate your help.
EDIT:
I tried many ways of getting a reference to the combo without success.
Now I am trying to use the Loaded event, but I get an error :
Failed to create a 'Loaded' from the text 'ComboBox_Loaded'
<ListView x:Name="listViewTarget" Height="560" Canvas.Left="10" Canvas.Top="101" Width="924" AllowDrop="True">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="test">
<Label x:Name="textFileName" Content="ABC" />
<TextBlock Text="DEF"/>
<ComboBox
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top"
Width="120"
Loaded="ComboBox_Loaded"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In Iron Python I define the event:
def ComboBox_Loaded(sender, e):
MessageBox.Show("Test")
EDIT 2:
Now I am trying to use the ListView CollectionChanged event
lstViewDropTarget.Items.CollectionChanged += ListView_ItemChanged
And here is the event code
def ListView_ItemChanged(sender, e):
MessageBox.Show(e.NewItems.Count.ToString())
That works messagebox shows '1'
So I can access the new item using e.NewItems[0]
But how do I access the controls?!
I try:
MessageBox.Show(e.NewItems[0].Children.Count.ToString())
and that fails to execute
so I try:
MessageBox.Show(e.NewItems[0].Content.Children.Count.ToString())
that also fails
I am very fed up, I cant believe its so very very hard

After trying many ideas and climbing the Iron Python learning curve I have a working solution. Whilst I have switched to a DataGrid, the method I have used would work with a ListView.
The first breakthrough came when I tried to bind the combobox to array within an iron python class. It just thought I would try and behold it worked!
There were further problems with SelectedItem and needing both a CellTemplate and a CellEditingTemplate, otherwise the selected item doesn't show when the combo loses focus.
I am quite pleased with the result, now I can just iterate through the DataGrid Items to save the results.
<DataGrid x:Name="gridUnknownDocuments" FontFamily="Segoe UI" FontWeight="Normal" FontSize="14" Height="294" Width="904" AutoGenerateColumns="False" SelectionUnit="Cell" Canvas.Left="20" Canvas.Top="39" >
<DataGrid.Columns>
<DataGridTextColumn Header="StepID" Binding="{Binding StepID}" IsReadOnly="True" />
<DataGridTextColumn Header="Document name" Binding="{Binding DocName}" IsReadOnly="True" />
<DataGridTemplateColumn Header="Awaiting Item">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="2" VerticalAlignment="Center" HorizontalAlignment="Left" Text="{Binding SelectedAwaitingItem, Mode=TwoWay}" Width="Auto" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox Height="23"
ItemsSource="{Binding Path= Items}"
SelectedValue="{Binding Path=SelectedAwaitingItem, Mode=TwoWay}" Width="Auto">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGrid.Columns>
IronPython Class:
class UnknownDocuments(object):
def __init__(self, StepID, DocName,Items,FromTypes):
self.StepID = StepID
self.DocName = DocName
self.SelectedAwaitingItem = ""
self.Items = Items
self.FromTypes=FromTypes
self.SelectedFromType = ""
Populating the DataGrid (from SQL Server):
def PopUnknownDocuments():
# Populate unknown docs grid
strSql="EXEC upLean_GetUnknownDocuments #EntityRef='"+_tikitEntity+"', #MatterNo="+_tikitMatter.ToString()
gridUnknownDocs = LogicalTreeHelper.FindLogicalNode(_tikitSender, 'gridUnknownDocuments')
_tikitDbAccess.Open(strSql)
if _tikitDbAccess._dr is not None:
dr = _tikitDbAccess._dr
if dr.HasRows:
item = []
while dr.Read():
if not dr.IsDBNull(0):
item.append(UnknownDocuments(dr.GetString(0), dr.GetString(1),["NA","Awaiting A","Awaiting B"],["Internal","Client","Other Side"]))
gridUnknownDocs.ItemsSource = item
dr.Close()

Related

UWP Bind to ObservableCollection declaratively

I have a simple UWP page; there is a public ObservableCollection in the page code-behind:
public ObservableCollection<Book> MyObservableColl{ get; } = new ObservableCollection<Book>();
(note the collection consists of a single item)
I would like to bind to the collection; here is what I tried:
<StackPanel x:Name="stackPanel"
Grid.Row="2"
Margin="50"
DataContext="{x:Bind MyObservableColl, Mode=OneWay}">
<TextBox x:Name="txtTitle"
TextWrapping="Wrap"
Text="{x:Bind Title, Mode=OneWay}" />
</StackPanel>
The visual studio designer complains "the property Title was not found".
I appreciate any help to bind my text boxes to the Observable Collection.
the property Title was not found
txtTitle TextBox can't access Book item where in the MyObservableColl collection directly, you need place txtTitle under items control's ItemTemplate.
For example
<ListView ItemsSource="{x:Bind MyObservableColl, Mode=OneWay}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Book">
<StackPanel>
<TextBox Text="{x:Bind Title}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
For more info please refer to listview document.
Update
For single item please refer to this xaml
<StackPanel
x:Name="stackPanel"
Grid.Row="2"
Margin="50"
DataContext="{x:Bind MyObservableColl[0], Mode=OneWay}">
<TextBox Text="{Binding Title}"/>
</StackPanel>

Binding from multi object in to one value in XAML

I have a ListView which have a DataTemplate like this:
<DataTemplate x:Key="FilterDataTemplate">
<StackPanel Orientation="Horizontal" Background="White" Height="50" HorizontalAlignment="Left">
<ComboBox x:Name="TermsItemComboBox" Width="160"
ItemsSource="{Binding ItemList}" Tag="{Binding}"
DisplayMemberPath="Key" SelectionChanged="AddTermsItem_SelectionChanged"
Background="#FFD1D1D1" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
<TextBox x:Name="TermsInputTxt1" Text="{Binding FilterText, Mode=TwoWay}"
BorderBrush="#FF727272" Background="#FFD1D1D1" HorizontalAlignment="Left"
Width="200" FontSize="16" VerticalAlignment="Center"
Visibility="{Binding IsTxtForm, Converter={StaticResource BooleanToVisibilityConverter}}"
Height="45"/>
<TextBox x:Name="TermsInputTxt2" Text="{Binding FilterText, Mode=TwoWay}"
BorderBrush="#FF727272" Background="#FFD1D1D1" HorizontalAlignment="Left"
Width="200" FontSize="16" VerticalAlignment="Center"
Visibility="{Binding IsTxtForm, Converter={StaticResource BooleanToVisibilityConverter}}"
Height="45"/>
<Button x:Name="TrashBtn" HorizontalAlignment="Left" VerticalAlignment="Stretch"
BorderBrush="#FF575757" Foreground="#FF494949" BorderThickness="2" Tapped="TrashBtn_Tapped"
Style="{StaticResource DiscardAppBarButtonStyle}" Height="45" Width="55"
RenderTransformOrigin="0.5,0.5">
<Button.RenderTransform>
<CompositeTransform ScaleX="-1" />
</Button.RenderTransform>
</Button>
</StackPanel>
It's have a combobox and two TextBox. At default, the combobox don's show anything, just a blank item, I want when user type some text to the TermsInputTxt1 or TermsInputTxt2, the combobox will select the first item.
I try to bind the selectedindex of combobox with the lenght of those two textbox with a converter but look like XAML doesn't support multi binding like that.
Any suggest? Thank you!
So, this is a bit of a difficult situation. You have a couple of ways of going about it:
Bind the TermsInputTxt1 and TermsInputTxt2 to the ViewModel, with the Setter on the ViewModel controlling an ObservableCollection, adding and removing items as they get set in the property.
Create a Model for each entry with a Value field (string) and bind the Terms... to accessors in the ViewModel. Then, the ComboBox items will automatically change.
I suggest doing the first one. You'll have to have a SelectedItem/SelectedIndex property in the ViewModel which the ComboBox will bind to. You may also want to change the method by which the binding on the TextBoxs are updated, either so that they are updated immediately (lots of processing) or after it loses focus (less processing, but won't update the UI immediately).

Howto correctly bind RibbonGalleryCategory to a collection

I'm using Microsoft.Windows.Controls.Ribbon.
I want to have a dynamic combobox with picture buttons in my ribbon.
If I do this directly in xaml, I get what I want:
<ribbon:RibbonComboBox
SelectionBoxWidth="62"
VerticalAlignment="Center"
>
<ribbon:RibbonGallery SelectedValue="0"
SelectedValuePath="Content"
MaxColumnCount="1">
<ribbon:RibbonGalleryCategory>
<ribbon:RibbonButton Label="Histo" HorizontalContentAlignment="Stretch"
Command="{Binding NewHistogrammCommand}"
SmallImageSource="/Test;component/Resourcen/Histogramm32.png"
LargeImageSource="/Test;component/Resourcen/Histogramm32.png" />
<ribbon:RibbonButton Label="3D" HorizontalContentAlignment="Stretch"
Command="{Binding NewDreiDCommand}"
SmallImageSource="/Test;component/Resourcen/DreiD32.png"
LargeImageSource="/Test;component/Resourcen/DreiD32.png" />
</ribbon:RibbonGalleryCategory>
</ribbon:RibbonGallery>
</ribbon:RibbonComboBox>
But if I try to do this via binding to a collection this way:
<ribbon:RibbonComboBox
SelectionBoxWidth="62"
VerticalAlignment="Center"
IsEditable="True" >
<ribbon:RibbonGallery
MaxColumnCount="1">
<ribbon:RibbonGalleryCategory ItemsSource="{Binding LayoutContentTypeList, ElementName=mainWindow}">
<ribbon:RibbonGalleryCategory.ItemTemplate>
<DataTemplate>
<ribbon:RibbonButton Label="{Binding Header}" HorizontalContentAlignment="Stretch"
Command="{Binding Command}"
CommandParameter="{Binding CommandParameter}"
SmallImageSource="{Binding ImageSource}"
LargeImageSource="{Binding ImageSource}" />
</DataTemplate>
</ribbon:RibbonGalleryCategory.ItemTemplate>
</ribbon:RibbonGalleryCategory>
</ribbon:RibbonGallery>
</ribbon:RibbonComboBox>
I get
System.Windows.Data Error: 40 : BindingExpression path error: 'IsDropDownOpen' property not found on 'object' ''ContentPresenter' (Name='')'. BindingExpression:Path=IsDropDownOpen; DataItem='ContentPresenter' (Name=''); target element is 'RibbonButton' (Name=''); target property is 'NoTarget' (type 'Object')
The buttons work correctly, but how can I resolve this binding error?
I'm guessing that you've found a solution for your problem, but for other people coming across this post, looking for an answer, you can find a complete solution that you can download and examine at your leisure in the How do I add Galleries to my Ribbon? post at 'The official blog of the Windows Presentation Foundation Team'. The basic idea is as follows.
Whatever object that you set as the RibbonGallery.DataContext should have a collection property to bind to the RibbonGalleryCategory.ItemsSource property. The objects in that collection should have properties containing the values that you want to appear in the gallery items. Declare a HierarchicalDataTemplate for the RibbonGallery.CategoryTemplate to bind your properties. Here is an example from the linked post:
<Ribbon:RibbonGallery DataContext="{x:Static data:WordModel.StylesParagraphGalleryData}"
ItemsSource="{Binding CategoryDataCollection}"
ScrollViewer.VerticalScrollBarVisibility="Hidden">
<Ribbon:RibbonGallery.CategoryTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding GalleryItemDataCollection}">
<Border Background="LightGray">
<TextBlock Text="{Binding}" FontWeight="Bold" />
</Border>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="Images\Paragraph_32x32.png" />
<TextBlock Margin="10,0,0,0" Text="{Binding}" />
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</Ribbon:RibbonGallery.CategoryTemplate>
</Ribbon:RibbonGallery>

Using different databinding sources within ListBox and ContextMenus

Here is the XAML:
<ListBox ItemsSource="{Binding Documents}" BorderBrush="{x:Null}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}" FontSize="12" FontWeight="Bold" />
<TextBlock Text="{Binding ID}" FontSize="10" FontStyle="Italic" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu ItemsSource="{Binding CategoryList}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Command="{Binding AddDocumentToCategoryContextMenuCommand}" Header="{Binding Category.Name}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
Ok so the ListBox's ItemSource is bound to the Documents collection in the VM and properly renders the Titles and IDs
The Context Menu's ItemSource is bound to the CategoryList collection in the VM and properly renders the list of categories.
The problem I have is with the Command Binding:
Command="{Binding AddDocumentToCategoryContextMenuCommand}"
Since the ItemSource for the ContextMenu is already set, it tries to get the AddDocumentToCategoryContextMenuCommand from CategoryList. Obviously the command is not there, it is a member of the VM.
I do not want any references to the VMs or Models in the XAML. Everything is constructed using Unity and VM-View is associated in App.xaml:
<Application.Resources>
<DataTemplate DataType="{x:Type vms:FeedViewModel}">
<views:FeedView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:DocumentsViewModel}">
<views:DocumentsView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:ManagementViewModel}">
<views:ManagementView/>
</DataTemplate>
<DataTemplate DataType="{x:Type dev:DevelopmentViewModel}">
<dev:DevelopmentView />
</DataTemplate>
</Application.Resources>
How can I databind to a member of the VM from within the ContextItem.
Thanks.
UPDATED edit #1 starts Here
Here is the updated xaml (but still not working but some insight gained):
<ListBox ItemsSource="{Binding Documents}" x:Name="Results" BorderBrush="{x:Null}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Title}" FontSize="12" FontWeight="Bold" />
<TextBlock Text="{Binding ID}" FontSize="10" FontStyle="Italic" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ContextMenu>
<ContextMenu ItemsSource="{Binding CategoryList}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Command="{Binding ElementName=Results, Path=DataContext.AddDocumentToCategoryContextMenuCommand}" Header="{Binding Category.Name}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
I have this example working for a simple example that does not use a ContextMenu. It appears that the ContextMenu (even though attached to the ListBox) is not part of the user control visual tree. The binding always comes back null / not found. I think the ContextMenu, because it is a floating "window" is constructed in its own tree and therefore cannot find the ListBox call "Results" in order to access the ListBox's DataContext.
Any thoughts on this? Any recommendations on how deal with?
Edit #2 Starts Here
In case you are are wondering, figured out the answer to the binding question:
This binding works:
Command="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.DataContext.AddDocumentToCategoryContextMenuCommand}
Hope this helps others with the same question.
One last update for completeness.
In order for the command to know which context menu item was clicked on, I had to change the xaml slightly (silly oversight):
<ListBox.ContextMenu>
<ContextMenu x:Name="Context" ItemsSource="{Binding CategoryList}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Command="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.DataContext.AddDocumentToCategoryContextMenuCommand}"
CommandParameter="{Binding Category.ID}"
Header="{Binding Category.Name}" />
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</ListBox.ContextMenu>
Again, hope this helps others.
Use the ElementName property of the binding to specify that. You'd end up with something like this:
Command="{Binding ElementName=ViewModelObject
Path=AddDocumentToCategoryContextMenuCommand}"

How to get control parent DataContext for element to element databinding?

Suppose I have a user control which datacontext is bound to a VM. This VM has a property for a list MyList.
Inside this user control I have a ComboBox, I want to set following kind of xaml
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel HorizontalAlignment="Stretch">
<sdk:DataGrid ItemsSource="{Binding YourList}" IsReadOnly="True" AutoGenerateColumns="False" >
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<!-- ...... -->
<sdk:DataGridTemplateColumn Header="User" >
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ElementName=LayoutRoot, Path=DataContext.MyList}" DisplayMemberPath="Value" SelectedValuePath="Key" SelectedValue="{Binding UserID}" ></ComboBox>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</StackPanel>
</Grid>
but it is not working.
How to resolve this problem?
This worked form me. This was the ItemSource for a ComboBox that was within a DataGrid:
ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=sdk:DataGrid},
Path=DataContext.Teams}">
Are you trying to get to the main VM from within the UserControl? take a look at this solution. http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx