UserControl in DataTemplate does not bind correctly - c#

I've been trying to create a custom menu. For this reason I wanted to use an ItemsControl in order to make it flexible. After hours of headache I figured out how to make it - kinda.
I have my custom ItemsControl "LiftMenu" (which is not yet much custom but standard) and an UserControl called "LiftItem". Last but not least I got the Model-class "LiftMenuItem".
By adding a new LiftMenuItem to the LiftMenu, it should display a new LiftItem-control as corresponding item. So far so good, I managed to get this working.
In that LiftItem-control I bind like I would in a normal DataTemplate: plain bindings with a path, nothing more. Normally this would work just fine because the DataTemplate already has it's context set to the model-type.
But now I just get an empty control that does nothing and shows nothing, because the bindings don't work.
I implemented it this way:
<menu:LiftMenu HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="200" Background="#80A8A8A8" Margin="5,0,0,0">
<menu:LiftMenu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</menu:LiftMenu.ItemsPanel>
<menu:LiftMenu.ItemTemplate>
<DataTemplate DataType="{x:Type menu:LiftMenuItem}">
<menu:LiftItem />
</DataTemplate>
</menu:LiftMenu.ItemTemplate>
<menu:LiftMenuItem Header="Test1"/>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="4"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Border x:Name="border" BorderBrush="{Binding LabelColor}" BorderThickness="1" HorizontalAlignment="Left" Height="Auto" Margin="0"
VerticalAlignment="Stretch" Width="4" Background="{Binding BorderBrush, ElementName=border}"/>
<TextBlock Text="{Binding Header}" TextTrimming="CharacterEllipsis" Margin="5,0,5,0" HorizontalAlignment="Left" VerticalAlignment="Center"
Foreground="White" />
<controls:ProgressRing x:Name="ring" Grid.Column="2" HorizontalAlignment="Right" Stroke="#ffff8000" VerticalAlignment="Center"
Minimum="0" Maximum="100" Value="{Binding ProcessValue}" IsIndeterminate="{Binding ProcessIndeterminate}" Visibility="{Binding ProcessVisibility}"
Width="20" Height="20" Radius="10" Margin="2,0,2,0" />
</Grid>
In the end there is no text, no border. Just the ProgressRing is visible.
How can I fix this? This ListItem-control should become similiar to a button, thus I need to do some styling (animation, ...). I can't do this within a normal DataTemplate, but I don't want to miss the binding features of WPF on that. This would make it relatively unflexible.
What's the problem? I probably just miss some DataContext or so, but I don't know what it would be.

Related

Column in UWP DataTemplate not stretching

I'm writing a UWP app to track TV shows watched/purchased/streamed etc and
am going absolutely crazy trying to get grid columns inside a DataTempate to stretch their width as it seems there is a bug in XAML which ignores the * width definition. I need the first column in the ListView (the show Title) to take up the remaining space (hence the column definition = "*") and while it will do that in the HeaderTemplate it absolutely refuses to do it inside the DataTemplate so the whole grid just ends up being all wonky and out of alignment as the Title column only uses the space it needs on each line.
My XAML is below - in the ItemTemplate DataTemplate template I am binding to an instance of an object called TVShow which is in an observable collection in my main view model. (I have not included the ViewModel or TVShow class definition here as I know this is a purely XAML issue).
The only thing that worked so far is having an extra property in my TVShow class that stores the correct width of the column (by subtracting the widths of the other three columns from the grid size (fetched in the view code behind) but this causes the whole list to reformat itself after initally displaying which looks ugly, not to mention awful programming.
So I'm looking for ideas on how to solve this - I could move the property for the correct column width in the main viewmodel but then how do I bind to that in the template given I am binding to "TVShow"? Or do I have to take the content out of the DataTemplate and put in a UserControl? I have wasted so much time on something that is so ridiculously simple - this bug seems to have been around since WPF so why haven't MS ever fixed this - very frustrating.
<HubSection Name="hsShows" Width="{Binding HubSectionWidth}" MinWidth="430" MaxWidth="640"
VerticalAlignment="Top" HorizontalAlignment="Stretch" Background="{StaticResource Dark}" >
<HubSection.Header>
<TextBlock Text="Shows" TextLineBounds="TrimToBaseline" OpticalMarginAlignment="TrimSideBearings"
FontSize="24" Foreground="{StaticResource Light}"/>
</HubSection.Header>
<DataTemplate x:DataType="local:MainPage">
<ListView Name="lvwShows"
Width="{Binding HubSectionGridWidth}"
Grid.Row="0"
Foreground="{StaticResource Light}"
Background="{StaticResource Dark}"
Margin="-14,20,0,0"
Loaded="lvwShows_Loaded"
ItemsSource="{Binding AllShows}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
IsSwipeEnabled="True"
IsItemClickEnabled="True"
SelectedItem="{Binding SelectedTVShow, Mode=TwoWay}"
SelectionMode="Single"
ScrollViewer.VerticalScrollMode="Enabled"
ScrollViewer.VerticalScrollBarVisibility="Auto">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.HeaderTemplate>
<DataTemplate>
<Grid Width="{Binding HubSectionGridWidth}" Height="Auto" Background="DarkGreen" Margin="15,5,5,5" HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Title" FontSize="16" FontWeight="Bold" Foreground="{StaticResource Bright}"
VerticalAlignment="Bottom" HorizontalAlignment="Left"
Tag="TITLE,ASC" Tapped="ShowsGridHeading_Tapped"/>
<TextBlock Grid.Column="1" Text="Seasons" FontSize="16" FontWeight="Bold" Foreground="{StaticResource Bright}"
VerticalAlignment="Bottom" HorizontalAlignment="Center"
Tag="SEASONS,ASC" Tapped="ShowsGridHeading_Tapped"/>
<TextBlock Grid.Column="2" Text="Last Watched" FontSize="16" FontWeight="Bold" Foreground="{StaticResource Bright}"
VerticalAlignment="Bottom" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"
Tag="WATCHED,ASC" Tapped="ShowsGridHeading_Tapped"/>
<TextBlock Grid.Column="3" Text="Last Episode" FontSize="16" FontWeight="Bold" Foreground="{StaticResource Bright}"
VerticalAlignment="Bottom" HorizontalAlignment="Center" TextAlignment="Center" TextWrapping="Wrap"
Tag="EPISODE,ASC" Tapped="ShowsGridHeading_Tapped"/>
</Grid>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:TVShow">
<Grid Height="Auto" MinWidth="410" MaxWidth="640" Background="Blue" HorizontalAlignment="Stretch" RightTapped="ShowsList_RightTapped">
<FlyoutBase.AttachedFlyout>
<MenuFlyout Placement="Bottom">
<MenuFlyoutItem x:Name="UpdateButton" Text="Update from TVMaze" Click="FlyoutUpdateButton_Click"/>
<MenuFlyoutItem x:Name="RefreshButton" Text="Refresh" Click="FlyoutRefreshButton_Click"/>
<MenuFlyoutItem x:Name="DeleteButton" Text="Delete Show" Click="FlyoutDeleteButton_Click"/>
</MenuFlyout>
</FlyoutBase.AttachedFlyout>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{x:Bind Title}" Foreground="{StaticResource Light}"
VerticalAlignment="Center" HorizontalAlignment="Stretch" TextWrapping="Wrap" />
<TextBlock Grid.Row="0" Grid.Column="1" Text="{x:Bind Seasons}" Foreground="{StaticResource Light}"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="0" Grid.Column="2" Foreground="{StaticResource Light}"
Text="{x:Bind LastWatchedDate, Mode=OneWay, Converter={StaticResource DateTimeFormatConverter}, ConverterParameter='{}{0:dd/MM/yyy HH\\\\:mm}'}"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
<TextBlock Grid.Row="0" Grid.Column="3" Text="{Binding LastWatchedEpisodeRef}" Foreground="{StaticResource Light}"
VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</HubSection>
Ok, so I ended up adding this XAML into my ListViews (though I know I could have done what Grace suggested but I just find Blend horrific to use) - it was the HorizontalContentAlignment that actually did the trick!
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
If you have Correct width in viewmodel you can Bind it like this
Width={Binding ElementName = ListViewname,Path=DataContext.width}
This problem is caused by the default template of the ListViewItem, to make the Grid stretch inside of the items, you can open the Document Outline label => find your ListView control and right click on it, then choose Edit Additional Templates => select Edit Generated Item Container (ItemContainerStyle), and at last Edit a Copy.
Then you will find this template in your Page resources, please change the code:
<Setter Property="HorizontalContentAlignment" Value="Left" />
To:
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
Then your problem can be solved.
I saw you've more then one ListView, if you want this style target all the ListView in this page, you can remove the x:Key attribute of this template and remove the ItemContainerStyle with the StaticResource which is generated by the action upper.
Don't be frustrating, I think you have developed WPF before, it's easy to learn UWP. Editing the template or the styles of the controls can solve many layout problem, here is some default templates and styles of different controls, you may take a look next time you have such problem.
If you have questions about how to develop an UWP app, you can refer to Develop UWP apps, and if you have some problems with the APIs, you may refer to Reference for Universal Windows apps.
If you need help or suggestion, you may ask question here, people here are glad to help.

Design Xaml grid with height = 100% and minimum row height size

Here's the problem. I have a grid with some data written in xaml:
<ItemsControl x:Name="ItemsControl" ItemsSource="{Binding Path=MyObjectCollection, UpdateSourceTrigger = PropertyChanged}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="27"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MaxHeight="75" MinHeight="30"></RowDefinition>
</Grid.RowDefinitions>
<Label Name="LabelNumber" Content="{Binding ObjectID}" Grid.Column="0" Style="{StaticResource MyStyleLabel}" />
<control:FocusMeterControl x:Name="HorizontalFocusMeterControl" Value="{Binding ObjectProperty}" Height="Auto" Grid.Column="1" />
<Button Name="RemoveObject" Content="-" Grid.Column="2" Margin="5,0,0,0" Click="ButtonBase_OnClick" Tag="{Binding Point}" Style="{StaticResource MyStyleButton}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
As you can see I'm creating a new grid for each member in my "MyObjectCollection". Ideally I think I should just create one row instead since this would make my next problem - the real problem easier.
However, I have not found any good way to do this in xaml if even possible. Is this possible without populating the collection manually from c# and setting a row-property in my objects manually in order to do something like this
<Label Name="LabelNumber" Grid.Row="{Binding ManuallyCalculatedRowID}" ... />
My primary problem is that I would like the grid/rows to all be equally high and if possible fill out the parent window. If the parent window is way too large MaxHeight should apply and I would just like some empty space below.
The parent control is a Windows Form ElementHost if that makes any difference.
Please let me know if you need any additional info.
To make all rows of equal hight we can use ItemsPanelTemplate and set it to UniformGrid that will take care of equal sizing problem:
<ItemsControl x:Name="ItemsControl" ItemsSource="{Binding Path=MyObjectCollection, UpdateSourceTrigger = PropertyChanged}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Width="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="27"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="30"/>
</Grid.ColumnDefinitions>
<Label Name="LabelNumber" Content="{Binding ObjectID}" Grid.Column="0" Style="{StaticResource MyStyleLabel}" />
<control:FocusMeterControl x:Name="HorizontalFocusMeterControl" Value="{Binding ObjectProperty}" Height="Auto" Grid.Column="1" />
<Button Name="RemoveObject" Content="-" Grid.Column="2" Margin="5,0,0,0" Click="ButtonBase_OnClick" Tag="{Binding Point}" Style="{StaticResource MyStyleButton}" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note that I removed row definition in ItemTemplate to lift vertical size restrictions for items. Not sure if that answers your question but that can be good starting point on the road to solution.

WPF ListBox SelectionBox

I have a DataTemplate for my WPF ListBox:
<DataTemplate DataType="{x:Type local:LogEntry}" x:Key="lineNumberTemplate">
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Cursor="/LogViewer;component/Template/RightArrow.cur">
<Rectangle Fill="{Binding Path=LineNumbersBackgroundColor, ElementName=LogViewerProperty}" Opacity="0.4" />
<TextBlock Grid.Column="0" Margin="5,0,5,0" Style="{StaticResource MyLineNumberText}" x:Name="txtBoxLineNumbers" />
</Grid>
<TextBlock Grid.Column="1" Margin="5,0,0,0" Style="{StaticResource MyTextEditor}" />
</Grid>
</DataTemplate>
Is it possible that the selection box begins not at the beginning (MyLineNumberText) but at MyTextEditor? Sorry I don't know how to describe it in the right way.
Yes, it is possible. You have to modify the style of the listbox. If you are using Blend this is easy. Otherwise you could get the style for Listbox and ListboxIten here: http://msdn.microsoft.com/en-us/library/cc278062(v=vs.95).aspx
Copy the style to your project and then change the style acordingly.

Binding different Target on loaded DataTemplateSelector

I am using a DataTemplateSelector to select different UserControls (reference http://tech.pro/tutorial/807/wpf-tutorial-how-to-use-a-datatemplateselector), according the selected path I select the needed UserControl.
The problem is now, when using the WebBrowser Control, I should bind it to ActualHight of MyScrollViewer, but on all others it works with Hight or else the scroll bar is displayed bad. Must come from the WebBrowser control.
How can I switch the Bindings in the ContentControl between Hight/ActualHight depending of the loaded UserControl?
<DataTemplate x:Key="WebTemplate1">
<DockPanel LastChildFill="True">
<controls:WebBrowserUserControl SourceHtml="{Binding Converter={StaticResource UriConverter1}}" />
</DockPanel>
</DataTemplate>
<DataTemplate x:Key="ImgTemplate1">
<Image Source="{Binding Converter={StaticResource RelativeToAbsolutePathConverter1}}"
Stretch="None" />
</DataTemplate>
...
<ScrollViewer Name="MyScrollViewer"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
DockPanel.Dock="Left"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<Grid x:Name="MyGridHelper">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<DockPanel x:Name="MyDockPanel" Dock="Top" HorizontalAlignment="Left">
<ContentControl x:Name="MyContentControl"
Width="{Binding ElementName=MyScrollViewer,
Path=Width/ActualWidth}"
Height="{Binding ElementName=MyScrollViewer,
Path=Height/ActualHight}"
Content="{Binding Path=CurrentItem1,
Mode=OneWay}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
</DockPanel>
</Grid>
</ScrollViewer>
The DataTemplateSelector by itself cannot affect the other properties of the ContentControl, however, you could use a Converter to determine the Width\Height based on the same logic used to determine which template to use. So something like this:
<ContentControl x:Name="MyContentControl"
Width="{Binding ElementName=MyScrollViewer, Converter={StaticResource MyWidthConverter}, ConverterParameter="???"}"
Height="{Binding ElementName=MyScrollViewer, Converter={StaticResource MyHeightConverter}, ConverterParameter="???"}"
Content="{Binding Path=CurrentItem1, Mode=OneWay}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}" />
I put question marks for the ConverterParameter because I'm not clear on how you determine which case calls for which Width/Height. But you can pass in a parameter that will allow you to decide which value to pass back, and based on that decision, the Converter can determine whether to get the ActualWidth/ActualHeight or the Width/Height of the ScrollViewer that is passed in.

How to apply telerik transition to a border that has been created in XAML code

I have the following XAML in my program
<Border x:Name="topCornerBorder" CornerRadius="10" Height="auto" Width="auto" Background="White">
<Grid x:Name="topCorner" Grid.Row="0" Grid.Column="0" Background="White" Margin="10,10,10,10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="190*"/>
<ColumnDefinition Width="30*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="270*"/>
<RowDefinition Height="60*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="StackPanel" Orientation="Vertical" Grid.Row="0" Grid.Column="1" >
<Canvas x:Name="textBlockCanvas1">
</Canvas>
</StackPanel>
<Canvas Grid.Row="1" Grid.Column="0" >
<sdk:DataGrid x:Name="dataGrid" Grid.Row="1" Grid.Column="0" Height="50" Width="300" Canvas.Top="15" Canvas.Left="100" Visibility="Collapsed" AutoGenerateColumns="False" ColumnWidth="*" RowBackground="Aqua"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center" CanUserResizeColumns="false" CanUserSortColumns="False" IsReadOnly="True" BorderThickness="3"
CanUserReorderColumns="False" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden">
<sdk:DataGrid.Columns>
<!--Column stuff here not important for this question..-->
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</Canvas>
</Grid>
and i tried to set the transition by doing the following:
<telerik:RadTransitionControl Name="radTransitionControl" Duration="00:00:01" Content="{Binding topCornerBorder}" >
<telerik:RadTransitionControl.Transition >
<telerik:SlideAndZoomTransition/>
</telerik:RadTransitionControl.Transition>
</telerik:RadTransitionControl>
But nothing is happening. I also tried the following in the C# code behind:
radTransitionControl.Content = this.topCornerBorder;
But this results in an error "Value does not fall within expected range".
What do i have to do to successfully set the transition content property to the border that surrounds the rest of my UI elements?
It looks like you are using the Transition Control incorrectly.
The control is simply a content control which triggers an animation when the content changes.
Typically, you would break visuals up into individual User Controls, and set the content of the Transition Control at the appropriate time.
In the simplest example, you might have two UserControls (View1.xaml and View2.xaml). From code you would set
radTransitionControl.Content = new View1();
Then you would set
radTransitionControl.Content = new View2();
In the second set operation, you would expect to see the transition occur.
Note that none of the transitions will happen if the setters are called before the Transition Control has been loaded.
Also note that this Content="{Binding topCornerBorder} doesnt work because topCornerBorder is an element, and not a property.
Content="{Binding ElementName=topCornerBorder}
Is syntactically correct but will probably result in an exception because topCornerBorder is already part of the visual tree.
Well i was able to apply the transition to the border (and everything else inside of it) by simply doing:
radTransitionControl.PrepareAnimation();
I don't know if that's the correct way to do it but it's working so far.

Categories