first of all sorry for the title. I know this is not so clear but i cannot think of anything else. If someone wants me to change it, give your suggestions please (and please note that downvoting is not the right solution here) thanks.
Now my problem.
I have an xml that look like this:
<scenarios-list>
<scenario name="Fuori casa" value="fuori-casa">
<lights>
<lights-group name="soggiorno">
<lights-item name="portone" outputChannelName="Luci|Appartamento|Portone|DO_Stato" inputChannelName="Luci|Appartamento|Portone|DI_On_Off">off</lights-item>
<lights-item name="soggiorno-principale" outputChannelName="Luci|Appartamento|Soggiorno_Principale|DO_Stato" inputChannelName="Luci|Appartamento|Soggiorno_Principale|DI_On_Off">off</lights-item>
<lights-item name="soggiorno-secondario" outputChannelName="Luci|Appartamento|Soggiorno_Secondario|DO_Stato" inputChannelName="Luci|Appartamento|Soggiorno_Secondario|DI_On_Off">off</lights-item>
<lights-item name="parete-zona-cucina" outputChannelName="Luci|Appartamento|Parete_Zona_Cucina|DO_Stato" inputChannelName="Luci|Appartamento|Parete_Zona_Cucina|DI_On_Off">off</lights-item>
<lights-item name="finestra-soggiorno" outputChannelName="Luci|Appartamento|Finestra_Soggiorno|DO_Stato" inputChannelName="Luci|Appartamento|Finestra_Soggiorno|DI_On_Off">off</lights-item>
</lights-group>
... (lots of <lights-group> here)
</lights>
... (lots of <lights> here)
</scenario>
...(lots of <scenario> here)
</scenarios-list>
I have a usercontrol in which the user can see and edit this scenarios:
<Grid Name="BaseGrid">
<Grid.Resources>
<XmlDataProvider **x:Name="ScenesXmlName"** x:Key="ScenesXml" XPath="scenari-list/scenario" Source="C:\Users\andrea\RSSReaderSubscriptions-5.xml"/>
</Grid.Resources>
<ComboBox Name="ScenariCombo"
**ItemsSource="{Binding Source={StaticResource ScenesXml}}" DisplayMemberPath="#name"**/>
<StackPanel>
<ListBox Name="LightsList" **ItemsSource="{Binding Source={StaticResource ScenesXml}, XPath=lights/lights-group}**">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Name="LightsExpander" **Header="{Binding XPath=#name}"** >
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Label **Content="{Binding XPath=ligths-item#name}"** />
<CheckBox />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</StackPanel>
</Grid>
So basically i want to:
load the <scenarios-list> into the combobox (displaying just the
scenario's name) [My code already does this]
load the <lights-group> list into the listbox basing on the selected combobox item (display only the name of the lights-group) [My code already does this even if i get this error:
System.Windows.Data Error: 43 : BindingExpression with XPath cannot bind to non-XML object.; XPath='lights/lights-group' BindingExpression:Path=; DataItem='XmlDataCollection' (HashCode=56809051); target element is 'ListBox' (Name='LightsList'); target property is 'ItemsSource' (type 'IEnumerable') XmlDataCollection:'MS.Internal.Data.XmlDataCollection'
inside every Expander load all <lights-item> that are included into the <lights-group> [I don't have the faintest idea on how to do this]
Can you help me please?
I managed to get i tworking with this code:
<Grid Name="BaseGrid">
<Grid.Resources>
<XmlDataProvider x:Name="ScenesXmlName" x:Key="ScenesXml" XPath="scenari-list/scenario" Source="myPath"/>
</Grid.Resources>
<ComboBox Grid.Column="0" Grid.Row="1" Name="ScenariCombo" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Source={StaticResource ScenesXml}}" DisplayMemberPath="#name"/>
<StackPanel>
<ListBox Name="LightsList" ItemsSource="{Binding Source={StaticResource ScenesXml}, XPath=lights/lights-group}">
<ListBox.ItemTemplate>
<DataTemplate>
<Expander Header="{Binding XPath=#name}" HorizontalAlignment="Stretch">
<ListBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding XPath=lights-item}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Label Content="{Binding XPath=#name}" Grid.Column="0"/>
<CheckBox Grid.Column="1" IsChecked="{Binding XPath=., Converter={StaticResource myStateToBoolConverter}}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Expander>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
So the key seems to be IsSynchronized="true".
Hope this is useful for other people.
Related
I am bit new to WPF (XAML) and I have an ItemsControl bound to a list with a MyCollections property . Now my requirement is initially to show only 1st element from the list and having a Show More button option at the end of list. A click on it would show the rest of the items from the collection.
This is my XAML so far, displaying the whole collection:
<ItemsControl x:Name="ContentRoot" ItemsSource="{Binding MyCollections}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<grid>
<TextBox Text="{Binding }" />
<TextBox Text="{Binding }" />
</grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You can have both a ContentControl (which displays the first item of the collection) and an ItemsControl (which displays the whole collection) displayed only when a ToggleButton is checked for example.
<StackPanel>
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<DataTemplate DataType="{x:Type local:MyViewModel}">
<Grid>
<TextBox Text="{Binding}" />
</Grid>
</DataTemplate>
</StackPanel.Resources>
<ContentControl Content="{Binding MyCollection[0]}"/>
<ToggleButton x:Name="toggle" Content="Show more"/>
<ItemsControl ItemsSource="{Binding MyCollection}" Visibility="{Binding ElementName=toggle, Path=IsChecked, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</StackPanel>
You can then adapt this to fit your exact needs.
Referencing this example:
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-bind-to-a-collection-and-display-information-based-on-selection
(some relevant code snippets:)
<Window.Resources>
<local:People x:Key="MyFriends"></local:People>
<DataTemplate x:Key="DetailTemplate">
<Border Width="300" Height="100" Margin="20"
BorderBrush="Aqua" BorderThickness="1" Padding="8">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=FirstName}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=LastName}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Home Town:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=HomeTown}"/>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<ListBox Width="200" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource MyFriends}}"/>
<ContentControl x:Name="contentControl1"
Content="{Binding Source={StaticResource MyFriends}}"
ContentTemplate="{StaticResource DetailTemplate}" />
Both ListBox.ItemsSource and ContentControl.Content bind to the same source (MyFriends, an instance of the People class which derives from ObservableCollection<Person>). If my understanding is correct, this means that both the ListBox.ItemsSource and ContentControl.Content properties will be bound to the same implicitly created instance of ListCollectionView.
I understand that setting ListBox.IsSynchronizedWithCurrentItem="True" synchronizes ListBox.SelectedItem and ItemCollection.CurrentItem.
DetailTemplate (above) displays the details of the selected ListBox item, despite being 'bound' to a ListCollectionView. Specifying Path=/ (what I thought would be necessary to achieve the resulting behavior) does not have any effect - it's as if WPF knows to do it implicitly somehow:
<ContentControl x:Name="contentControl1"
Content="{Binding Source={StaticResource MyFriends}, Path=/}"
ContentTemplate="{StaticResource DetailTemplate}" />
As a test, I created another ContentControl with Content bound to a DataTemplate containing a ListBox:
<ContentControl x:Name="contentControl2"
Content="{Binding Source={StaticResource MyFriends}}"
ContentTemplate="{StaticResource DetailTemplate2}" />
<DataTemplate x:Key="DetailTemplate2">
<ListBox ItemsSource="{Binding}"></>
</DataTemplate>
And it displayed the list.
My question is: Why does DataTemplate get the selected Person object while the ListBox and DetailTemplate2 get the People collection?
(the behavior is desirable, I just don't understand what black magic is occurring under the hood to make it so)
Is a good question! I didn't notice that until read your post. So, after did some digging from source code of PropertyPathWorker, it appears that when PropertyPathWorker failed to solve a member of an object, in your case, it try to solve 'FirstName', 'LastName' ect. with 'MyFriends', it will try to solve it with the view of the object. And if still failed, it will try to solve it with view's CurrentItem, and that's where the magic happened. You can find those codes in PropertyPathWorker.UpdateSourceValueState(int k, ICollectionView collectionView, object newValue, bool isASubPropertyChange) and PropertyPathWorker.ReplaceItem(int k, object newO, object parent).
I have the following code that creates a TabControl. Each tab contains a UserControl (code is below) that displays different data (one shows Local tax info and the other show Fed/State tax info).
TabControl
<TabControl
Name="MappingTabs"
Margin="6,7,7,8" Padding="6"
Background="White" >
<TabItem
Name="LocalTaxTab"
Padding="6,1"
Header="Local">
<AdornerDecorator>
<DockPanel>
<Border Margin="7">
<GroupBox
Name="LocalTaxesGroup">
<GroupBox.Header>
<TextBlock
FontWeight="Bold"
Text="Local Taxes">
</TextBlock>
</GroupBox.Header>
<StackPanel Margin="20,8,10,0"
Orientation="Vertical">
<local:TaxCodeMappingHeader />
<!-- Note that a row is 25 high, -->
<ScrollViewer
MaxHeight="250"
>
<ItemsControl
Name="LocalTaxCodeMappingControl"
ItemTemplate="{StaticResource MappingRuleTemplate}"
BorderThickness="0"
AlternationCount="2"
IsTextSearchEnabled="False"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding TaxCodesCollection[0].CodeCollection, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<!-- ItemsSource="{Binding Source={StaticResource sortedCodeCollection}}"> -->
</ItemsControl>
</ScrollViewer>
<local:TaxCodeMappingFooter DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel>
</GroupBox>
</Border>
</DockPanel>
</AdornerDecorator>
</TabItem>
<TabItem
Name="FedStateTaxesTab"
Padding="6,1"
Header="Federal\State">
<AdornerDecorator>
<DockPanel>
<Border Margin="7">
<GroupBox
Name="FedStateTaxesGroup">
<GroupBox.Header>
<TextBlock
FontWeight="Bold"
Text="Federal \ State Taxes">
</TextBlock>
</GroupBox.Header>
<StackPanel Margin="20,8,10,0"
Orientation="Vertical">
<local:TaxCodeMappingHeader />
<!-- Note that a row is 25 high, -->
<ScrollViewer
MaxHeight="250"
>
<ItemsControl
Name="FedStateTaxCodeMappingControl"
ItemTemplate="{StaticResource MappingRuleTemplate}"
BorderThickness="0"
AlternationCount="2"
IsTextSearchEnabled="False"
HorizontalContentAlignment="Stretch"
ItemsSource="{Binding TaxCodesCollection[1].CodeCollection, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}">
<!-- ItemsSource="{Binding Source={StaticResource sortedCodeCollection}}"> -->
</ItemsControl>
</ScrollViewer>
<local:TaxCodeMappingFooter DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}"/>
</StackPanel>
</GroupBox>
</Border>
</DockPanel>
</AdornerDecorator>
</TabItem>
</TabControl>
</StackPanel>
UserControl (TaxCodeMappingFooter)
<Button
Name="AddButton"
Grid.Row="0"
Grid.Column="0"
Height="20" Width="20"
Command="{Binding Path=DataContext.AddClickCommand}"
CommandParameter="(want the tab name here)"
Style="{StaticResource ImageButton}"
ToolTip="Add a rule"
local:AttachedImage.Image="{StaticResource AddImageSource}" />
The UserControl (TaxCodeMappingFooter) contains an Add button that I need to wire up via RelayCommand to the VM. I need to somehow tell the VM which tab is calling the Add command so that an item can be added to the correct collection. I thought about sending the TabName and then keying off that to know which tab the user is on.
Is my idea correct or is the a better way to do this and if it is correct how do I get the TabName value to pass it back as a CommandParameter?
If you are going to hard code your UI controls as you have done, then perhaps your simplest option is to define a string DependencyProperty in your TaxCodeMappingFooter control:
public static readonly DependencyProperty TabNameProperty = DependencyProperty.
Register("TabName", typeof(string), typeof(TaxCodeMappingFooter));
public string TabName
{
get { return (string)GetTabName(TabNameProperty); }
set { SetTabName(TabNameProperty, value); }
}
Then you could set it from your TabItems:
<local:TaxCodeMappingFooter TabName="FedStateTaxesTab" DataContext="{Binding
RelativeSource={RelativeSource AncestorType=UserControl}}" />
And Bind to it from inside your control:
<Button Name="AddButton" Command="{Binding Path=DataContext.AddClickCommand}"
CommandParameter="{Binding TabName, RelativeSource={RelativeSource
AncestorType=TaxCodeMappingFooter}}" ... />
As others have said, if you model your view model structure appropriately, this would not be much of an issue.
If you really want to bind against an ancestor element, you can use a RelativeSource of FindAncestor, then specify the AncestorType. Note that you may need to tweak AncestorLevel if you are the descendant of more than one TabItem.
{Binding Path=Name
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type TabItem}}}
(wrapping added for clarity)
I have the following XAML code and the only element available in the C# code behind are the Grid and the FlipView. How can I make the ScrollViewer, Image or the Viewbox visibile in the code?
XAML:
<Grid x:Name="gridViewPages">
<FlipView x:Name="FlipView1" Loaded="FlipView1_Loaded" Style="{StaticResource FlipViewPreviewIndicator}" Tapped="FlipView1_Tapped">
<FlipView.ItemTemplate>
<DataTemplate>
<ScrollViewer x:Name="pagesScrollViewer" ZoomMode="Enabled"
HorizontalAlignment="Center"
VerticalAlignment="Center"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
MinZoomFactor="1.0"
MaxZoomFactor="3.0"
Margin="0"
Width="1500" DoubleTapped="PagesScrollViewer_DoubleTapped">
<Viewbox x:Name="pagesViewbox">
<Image Source="{Binding}"
Height="730"
x:Name="pageImage" Stretch="Uniform" Loaded="MainImage_Loaded"/>
</Viewbox>
</ScrollViewer>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
</Grid>
The flipview is customized and contains also a listview defined in which is not visible in the code too...:
<Page.Resources>
...
<ListView x:Name="pagesPreview" HorizontalAlignment="Center" Height="100" VerticalAlignment="Bottom" Width="Auto"
ItemsSource="{TemplateBinding ItemsSource}"
SelectedItem="{Binding SelectedItem, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.VerticalScrollMode="Disabled"
Background="AliceBlue"
Opacity="1"
SelectionChanged="pagesPreview_SelectionChanged"
Visibility="Visible">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Image Source="{Binding}" Stretch="UniformToFill"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
...
</Page.Resources>
See the basic concept of the flipview is to have multiple pages to have a flip effect. So whatever the Data template contains is repeated multiple times. So if you want the x:Name to come up then you wont have any success.
There are two ways As per my knowledge :
VisualTreeHelper -> for a better detail of it go through this link
Get elements from data template
you can manually iterate over the flipview elements and track down the children of the flipview. Try that in debug mode you'll get a fair idea of what comes after what. Just keep track of the element at which selected index position you want modified
Thanks.
I need to change Pivot header background and padding (top menu of Pivot control where are titles of pivot items).
I try this:
<controls:Pivot Grid.Row="1" Margin="0" Padding="0">
<controls:Pivot.HeaderTemplate>
<DataTemplate>
<StackPanel Background="#666666" Margin="0" />
</DataTemplate>
</controls:Pivot.HeaderTemplate>
<controls:PivotItem Background="#65006a">
<controls:PivotItem.Header>
<TextBlock FontSize="60" Margin="0">tasks</TextBlock>
</controls:PivotItem.Header>
</controls:PivotItem>
</controls:Pivot>
But it doesnt solve it.
How can I do this?
The proper way to do it is with the following snipped:
<controls:Pivot.HeaderTemplate>
<DataTemplate>
<StackPanel Background="#666666" Margin="0">
<TextBlock FontSize="60" Margin="0" Text="{Binding}"/>
</StackPanel>
</DataTemplate>
</controls:Pivot.HeaderTemplate>
<controls:PivotItem Background="#65006a" Header="tasks">
</controls:PivotItem>
Each PivotItem has header text which is then passed to the data template contained in HeaderTemplate. You can see that I've moved TextBlock inside StackPanel and set its text via binding. What you did wrong is that header template contained only StackPanel and nothing inside it.
Here, I have modified the header of a pivot control. I have defined different control with different colours so you can easily identify what I have done. Modify this source as per need to change pivot header.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:Pivot>
<phone:Pivot.HeaderTemplate>
<DataTemplate>
<StackPanel Background="Blue"
Margin="15,-10,0,0">
<TextBlock Text="{Binding}"
FontSize="20"
/>
</StackPanel>
</DataTemplate>
</phone:Pivot.HeaderTemplate>
<phone:PivotItem Header="item1">
<Grid Background="Red"></Grid>
</phone:PivotItem>
<phone:PivotItem Header="item2">
<Grid Background="Green"></Grid>
</phone:PivotItem>
</phone:Pivot>
</Grid>