On the main window xaml, I have a Frame that will host 2 pages, like this
<Window x:Class="Monitor.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" FontSize="14">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="48"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Frame Content="Frame" Grid.Row="1" Source="/Monitor;component/Views/Pages/GroupPage.xaml"/>
</Grid>
</Window>
On the first page (GroupPage.xaml) xaml code is
<Page.DataContext>
<VM:GroupPageVM />
</Page.DataContext>
<Grid>
<ItemsControl x:Name="GroupsOfItemsControl" ItemsSource="{Binding GroupsOfItems}">
<ItemsControl.Template>
<ControlTemplate>
<WrapPanel Width="{TemplateBinding Width}" Height="{TemplateBinding Height}"
FlowDirection="LeftToRight" IsItemsHost="true">
</WrapPanel>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button HorizontalAlignment="Right" Margin="3"
Content="{Binding Name}"
Width="1.2in" Height=".75in" FontSize="14"
Command="{Binding Path=GroupSelectedCommand }"
CommandParameter= "{Binding}"
/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
The GroupPageVM is the VM for this page. GroupsOfItems is a collection of VM for groups. Each Group VM has a Name where I bind it for the content of a button that represent for that group. So on the screen, I will see a collection of buttons in a WrapPanel. Everything display correctly.
Now the issue come with the command that handle the click on the button, the code expect I put my Command (GroupSelectedCommand) and its execute function inside the VM of the Group, instead in the MainWindow VM.
Can someone explain why?
If it is how it suppose to work, how do I put the command in the MainWindow VM? because without that I cannot access to the Frame that host the pages or any NavigationService to go to page 2 by clicking on one of those buttons.
(My simple goal is clicking on a button that represent a group, and it will navigate to a page to display items in that group)
You have bound GroupsOfItems as the ItemsSource of the ItemsControl. Inside GroupsOfItems you have GroupVM objects. So each item inside the ItemsControl will be holding a GroupVM object. Also the DataTemplate defined will be representing the GroupVM object. Thus the Command will be expected inside GroupVM object.
To access parent DataContext, you can try giving ElementName as shown below,
Command="{Binding DataContext.GroupSelectedCommand, ElementName=GroupsOfItemsControl }"
In the above code the button will try to take the Command from the DataContext of ItemsControl which is the GroupPageVM
Related
Each UserControl I have in a stackPanel has a WrapPanel, and each UserControl in this WrapPanel is a word (so they have a different size from each other, depending on the length of the word), so that a sentence appears.
Here is an image to help you understand better:
In pink it is the UserControl "sentence" that contain each of them ONE wrapPanel
In Green it is all the UserControl "word" that have all different size according to the word length.
Here is the UserControl "word":
<UserControl x:Class="Coran_seul.UC.UserControl_WordInVerse"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Coran_seul.UC"
mc:Ignorable="d" Margin="5,0,5,0">
<StackPanel>
<TextBlock x:Name="textBlock_arabic" Text="ٱلْأَنفَالُ " FontSize="20" HorizontalAlignment="Center" FontFamily="Noto Naskh Arabic" MouseEnter="textBlock_arabic_MouseEnter" MouseLeave="textBlock_arabic_MouseLeave" Cursor="Hand" MouseDown="textBlock_arabic_MouseDown"/>
<TextBlock x:Name="textBlock_french" Text="Les surplus (de bénéfice)" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</StackPanel>
And here is the UserControl "sentence" (with the wrapPanel I have a problem with)
<UserControl x:Name="userControl" x:Class="Coran_seul.UC.UserControl_VerseInSurah"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Coran_seul.UC"
mc:Ignorable="d" Margin="0,5,0,5"
>
<Border x:Name="Border_Verse" >
<StackPanel Orientation="Horizontal" x:Name="grid">
<Label x:Name="Label_VersetNum" Content="2" Height="{Binding ActualHeight, ElementName=WrapPanel_Mots, Mode=OneWay}" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" VerticalAlignment="Stretch" HorizontalAlignment="Left" MouseDown="Label_VersetNum_MouseDown"/>
<WrapPanel x:Name="WrapPanel_Mots" Width="{Binding ActualWidth, ElementName=userControl, Mode=OneWay}" />
</StackPanel>
</Border>
The problem :
Now that I've entered the code, here's the problem I'm having with my wrapPanel:
The last word of each line does not wrap, even if it is larger than the space left (= it is therefore hidden/cut off at the edge of the wrapPanel)
Again I have a video to better explain the problem :
https://streamable.com/lpdf38
I don't know what it's due to and I'm desperately looking since yesterday not to have this problem anymore, knowing that the stackPanel containing all the userControls has a right margin of 20 so that it's not hidden behind the scrollbar.
Please help me to find a solution so that the word as long as it is hidden even by one pixel is wrapped to the next line.
Thank you,
The problem is that you bind the WrapPanel's Width to that of its UserControl parent without subtracting the Width of the Label element.
You should not need to bind any Width at all, when you use suitable Panel elements, like e.g. a Grid or a DockPanel instead of StackPanel.
<Border x:Name="Border_Verse">
<DockPanel>
<Label x:Name="Label_VersetNum" DockPanel.Dock="Left" .../>
<WrapPanel x:Name="WrapPanel_Mots">
</DockPanel>
</Border>
<Border x:Name="Border_Verse">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label x:Name="Label_VersetNum" Grid.Column="0" .../>
<WrapPanel x:Name="WrapPanel_Mots" Grid.Column="1">
</DockPanel>
</Border>
You may also consider to use an ItemsControl to display a sentence. It would use a WrapPanel as its ItemsPanel and the word UserControl in its ItemTemplate:
<ItemsControl ItemsSource="{Binding Words}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<uc:UserControl_VerseInSurah/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
where Words is a collection of word data item objects in the view model, i.e. the object in the DataContext of the view. The two TextBlocks in the word UserControl would have their Text property bound to an appropriate property on the word data item class.
You may not even need the word UserControl at all, since you can simply move the elements from its XAML into the DataTemplate that is used as ItemTemplate.
See Data Templating Overview for details.
on uwp, I want to make a hamburger menu with custom icon in xaml. from the lunch scheduler sample from MS, I try to bind a viewmodel property to a content element in a view like :
<ListBox ItemsSource="{Binding Items}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate x:DataType="viewmodels:MenuItem">
<StackPanel Orientation="Horizontal">
<ContentControl Content="{x:Bind Icon2}" />
where Icon is a property like
Icon2 = Application.Current.Resources["BookIcon"],
which works and retreive a viewbox containing a canvas
but I got an exception "Value does not fall within the expected range."
Did some know if it's possible like in WPF ??
nb : if i put the canvas directly in the contentcontrol, it works. perhaps the binding does not accept anything else than string
i found a solution, convert the viewbox to DataTemplate and bind to ContentTemplate
<DataTemplate x:Key="BookIcon">
<Viewbox Width="48" Height="48">
<Canvas Width="24" Height="24">
<Path Data/>
</Canvas>
</Viewbox>
</DataTemplate>
and
<ContentControl ContentTemplate="{x:Bind Icon2}"/>
any other solution is welcome
I have a listbox, where a grid with textboxes shows the listboxs currently selecteditems data. (Code below)
The problem is that i want a delete button inside this grid, however the command it uses lies in the Usercontrols DataContext. How do I make the button recieve the command binding inside the UserControls Datacontext?
Following code lies inside a grid where the visual trees root is a UserControl.
<ListBox Name="list" ItemTemplate="{StaticResource listTemplate}" ItemsSource="{Binding listCollection, UpdateSourceTrigger=PropertyChanged}">
</ListBox>
<Grid DataContext="{Binding ElementName=list, Path=SelectedItem}" Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Margin="7">Name:</TextBlock>
<TextBox Margin="5" Grid.Column="1" Text="{Binding Path=Name}"></TextBox>
<Button Grid.Row="1" Command="{Binding DeleteSelectedItemCommand}">Delete Selected Item</Button>
</Grid>
You need to change DataContext, provide same Name for UserControl and then
Command="{Binding ElementName=Name, Path=DeleteSelectedItemCommand}"
You mean you want to fire the button's command on your UserControls DataContext IE your vm?
Then you need this binding;
<Button Grid.Row="1" Command="{Binding Path=DeleteSelectedItemCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type YourControl}}}">Delete Selected Item</Button>
Basicly this just traverse up your xaml tree and fetch datacontext from the control called YourControl. You could set UserControl here. The cool thing about this kind of binding is that you may track back the datacontext of any items up the chain :)
This document is still very handy for bindings that are a bit tricky :)
Cheers,
Stian
The trick was to wire up the DataContext using Stians reference.
<Button
DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType {x:Type UserControl}}, Path=DataContext}"
Command="{Binding Path=DeleteSelectedItemCommand}">
</Button>
I'm activating new item in conductor using Caliburn.Micro.Contrib's ConductResult. Conductor is of type Conductor<IScreen>.Collection.OneActive, and there is already one item showing and working correctly.
The new item however is not shown after it was activated. I already checked and the conductor's ActiveItem is set to that new item, new item is activated as well. View's IsVisible of new item is also set to true, so I don't understand why it is not visible.
XAML of the conductor's view is pretty simple:
<UserControl x:Class="..."
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:cal="http://www.caliburnproject.org"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Path=ActiveItem, Converter={StaticResource objectTypeConverter}}" Margin="5" />
<ItemsControl Grid.Row="1" ItemsSource="{Binding Items}" BorderBrush="Aqua" BorderThickness="10 ">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Converter={StaticResource objectTypeConverter},ConverterParameter=something}" Margin="5" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ContentControl Grid.Row="2" x:Name="ActiveItem" />
</Grid>
</UserControl>
(TextBlock and ItemsControl are there for debugging purposes; they prove that new item is conducted within conductor (i.e. Items collection contains it) and new item is set as ActiveItem)
In my case, I was using IoC.Get<IShell> to access the parent viewmodel.
The default bootstrapper says container.PerRequest<IShell, ShellViewModel>();
That’s why I was getting another instance of my ShellViewModel class, activated the item just fine, but no UI was updating.
One way to fix is replace container.PerRequest with container.Singleton.
Another is change IoC.Get<IShell>() into ( (IShell)Parent )
I am writing an application in which I utilize a tab control which will start with one tab open but allows the user to open multiple other tabs.
Each tab that is openned should have a treeview inside which I fill using databinding when the user loads a file.
I am new to WPF but I feel as if there is a way in which I can create a template containing each of the elements the TabItems should contain. How can I do this using templates? Right now my WPF for the tab items is the following
<TabItem Header="Survey 1">
<TreeView Height="461" Name="treeView1" VerticalAlignment="Top"
Width="625" Margin="0,0,6,0" ItemTemplateSelector="{StaticResource TreeviewDataSelector}" />
</TabItem>
I think you want something like this:
<Window x:Class="TestWpfApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="TabItemTemplate">
<TreeView Height="461" Name="treeView1" VerticalAlignment="Top"
Width="625" Margin="0,0,6,0" ItemTemplateSelector="{StaticResource TreeviewDataSelector}" />
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding ListThatPowersTheTabs}"
ItemTemplate="{StaticResource TabItemTemplate}">
</TabControl>
</Grid>
You basically create re-usable templates as static resources which you refer to by their key name.
Usually in this sort of situation I bind my TabControl.ItemsSource to a ObservableCollect<ViewModelBase> OpenTabs, so my ViewModel is in charge of adding/removing new tabs as needed.
Then if you want something in every Tab, then overwrite the TabControl.ItemTemplate and use the following line to specify where to display the currently selected TabItem
<ContentControl Content="{Binding }" />
If you don't need to setup something in every single tab, you don't need to overwrite the TabControl.ItemTemplate - it will default to displaying the currently selected ViewModelBase in the Tab.
And I use DataTemplates to specify which View to use
<DataTemplate TargetType="{x:Type local:TabAViewModel}">
<local:TabAView />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TabBViewModel}">
<local:TabBView />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TabCViewModel}">
<local:TabCView />
</DataTemplate>