Silverlight Binding To A User Control Within A DataTemplate - silverlight

In Silverlight, I have a DataTemplate which is bound to an object which contains a property which holds a UserControl.
Within the DataTemplate, I want to bind to the property which holds the UserControl so that the UserControl is displayed as part of the DataTemplate.
At the moment, I am using an ItemsControl and binding the ItemsSource to the property containing the UserControl and this is working, however, the UserControl is not filling the available space and this is making me wonder whether there is a better way of doing this.
Thanks for any help.
Martyn.
EDIT: As requested some XAML:
<DataTemplate x:Key="ContentTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<TextBlock Text="Large Content" Grid.Row="0"/>
<ItemsControl ItemsSource="{Binding Contents}" Grid.Row="1" MinHeight="200" MinWidth="300" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"/>
</Grid>
</DataTemplate>
Where, the Contents property being bound to is as follows:
private UserControl _contents;
public UserControl Contents
{
get {return _contents;}
set
{
_contents = value;
NotifyPropertyChanged("Contents");
}
}

Don't know why you're using an ItemsControl to show the content, maybe if you try it with a ContentControl.
<ContentControl Content="{Binding Contents}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" ...
Note the the properties HorizontalContentAlignment and VerticalContentAlignment, these properties sets the alignament of the control content, so if you set them to "Stretch" then the content should fit all the available space.

Related

WPF XAML treeview in a stack panel - no scrolling?

so I have a TreeView control in my XAML. It works fine. If I extend the treeview to be larger than the user control it resides in, I get a scroll bar, which is good. However, inside this user control I want some other things. So I put the treeview in a stack panel with some other things, and this time I don't get the scroll bar if the treeview expands to be larger than the user control it's in.
Is this something other people have come across, and is there a fix for it?
Embed your stackpanel inside a ScrollViewer: stackoverflow.com/a/6250287/7517676. You also might have to explicitly set VerticalScrollBarVisibility and HorizontalScrollBarVisibility, depending on your need.
Here's a code sample:
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel ... />
</ScrollViewer>
Based on this answer a StackPanel isn't the right container for a TreeView, but a Grid is. So this will enable scrolling inside the TreeView, by mouse and scrollbar:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label>Some descriptive label.</Label>
<TreeView Grid.Row="1" ItemsSource="{Binding SomeSource, Mode=OneWay}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MyNodeType}" ItemsSource="{Binding Children}">
<Label Content="{Binding NodeName}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>

WPF ContentControl does not collaspe on Visibility change

I have a tab sequence that I want to be able to split into two regions or recombine into a single tab sequence. The application will start off with one tab sequence
Allow the user to split into a second panel
And recombine into a single tab control.
At this stage, I should get the same display at the first image. But, instead, the ContentControl does not Collaspe and space is left in the main Grid. There is also a GridSplitter that devides the two areaa (white bar in image 2). That seems to Collapse as expected. Is there some trick to get it to get the ContentControl to Collapse?
Here's the XAML. There's a boolean property BottomTabDisplayed which indicates if there are any controls in the bottom area:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentControl x:Name="ControlContainerTopTab" Grid.Row="0">
<TabControl SelectedIndex="{Binding SelectedIndex}"
>
<TabControl.ItemContainerStyle>
...
</TabControl.ItemContainerStyle>
</TabControl>
</ContentControl>
<GridSplitter x:Name="GridSplitterTabControls"
Grid.Row="1"
Height="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Visibility="{Binding BottomTabDisplayed,
Converter={StaticResource VisiblityToBoolConverter}}" />
<ContentControl Grid.Row="2" Visibility="{Binding BottomTabDisplayed, Converter={StaticResource VisiblityToBoolConverter}}">
<TabControl SelectedIndex="{Binding SelectedBottomIndex}"
Visibility="{Binding BottomTabDisplayed,
Converter={StaticResource VisiblityToBoolConverter}}"
>
<TabControl.ItemContainerStyle>
...
</TabControl.ItemContainerStyle>
</TabControl>
</ContentControl>
</Grid>
Edit 1:
And, here's the converter
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool isVisible = (bool)value;
return (isVisible ? Visibility.Visible : Visibility.Collapsed);
}
I just noticed something interesting, if the GridSplitter is unchanged, it works as expected. If I move the GridSplitter, then I get the empty space.
Edit 2:
Snoop changes when moving the GridSplitter
If you look at your code for VisiblityToBoolConverter, you will very likely see it is returning Hidden instead of Collapsed

ListBox with customized Item in VB.NET

I want to modify the original ListBox control that the each item to have a CheckBox, Labels and a Button control inside.
Is there any optimal method to make that? without making Custom Control from the very beginning?
Making custom control that inherits ListBox could be not a bad idea, but don't know how...
Thank you!
I tried WPF but it was too difficult at this time. Actually, designing the control via XAML was easy, but managing the list items(add/delete with texts, get event from the button in each item) wasn't.
Since the question is tagged [WPF] I'm going to provide a WPF answer:
The first thing any developer who faces WPF immediately tries to do is to use it as if it were winforms. This is a big mistake.
If you're working with WPF, you really need to leave behind the traditional aproach used in archaic technologies such as winforms, and understand and embrace The WPF Mentality.
in WPF, you don't "add/delete with texts, get event from the button in each item" or any of that, simply because UI is not Data.
Instead, you define a simple Data Model:
public class MyData
{
public string MyText1 {get;set;}
public string MyText2 {get;set;}
}
and then declaratively define Data Bindings in the UI to "show" this data to the UI as opposed to "reading" or "writing" data to/from the UI:
<Window x:Class="WpfApplication14.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication14"
Title="MainWindow" Height="350" Width="525">
<ListBox ItemsSource="{Binding}" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Border Margin="5" Background="LightCyan" BorderBrush="LightSkyBlue" BorderThickness="2">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Text 1:" HorizontalAlignment="Right"/>
<Label Grid.Row="1" Grid.Column="0" Content="Text 2:" HorizontalAlignment="Right"/>
<TextBox Grid.Row="0" Grid.Column="1" Text="{Binding MyText1}"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding MyText2}"/>
<Button Grid.Row="2" Grid.ColumnSpan="2" Content="Button" HorizontalAlignment="Center"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Window>
And finally, you define the DataContext of the Window or View to a relevant instance or collection of such data:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = Enumerable.Range(0,10)
.Select(x => new MyData()
{
MyText1 = "Text1 - " + x.ToString(),
MyText2 = "Text2 - " + x.ToString()
});
}
}
All this results in:
See? really simple and beautiful.
Forget winforms, WPF Rocks. Just copy and paste my code in a File -> New Project -> WPF Application and see the results for yourself.
The best way to do this, short of using WPF, is to create a custom UserControl which represents each item that will go in the list. Then, add a FlowLayoutPanel to your form. Set the panel's AutoScroll property to True. Then set its FlowDirection property to TopToBottom. Then, dynamically create one of your custom controls for each item that you need to add to the list and call the panel's Controls.Add method to add them to the list.

WPF Listbox & MVVM binding

I am following MVVM in C# and am attempting to display a view in a listbox.
I am setting the listbox itemsource (in code, not in binding and using the viewmodels collection) and then set the datatemplate to be my view in xaml. The issue I'm encountering is my view always loads with its default constructor values, if I remove the datacontext from the view however it loads fine.
Below is the listbox I am creating in xaml
<ListBox Name="lbCatalogues" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<view:CatalogueRowView/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is the xaml for my view. If I remove the DataContext it works
<UserControl.DataContext>
<model:CatalogueModel />
</UserControl.DataContext>
<Grid Name="Container" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<!-- Catalogue_ID, UploadedTime, Client_ID, Name, Desc, Filesize -->
<Label Name="lblCatalogueID" Content="{Binding Path=CatalogueID}" Grid.Column="0"/>
<Label Name="lblUploadedTime" Content="{Binding Path=UploadedTime}" Grid.Column="1"/>
<Label Name="lblCatalogueName" Content="{Binding Path=Name}" Grid.Column="2"/>
<Label Name="lblCatalogueDescription" Content="{Binding Path=Description}" Grid.Column="3"/>
<Label Name="lblFilesize" Content="{Binding Path=Filesize}" Grid.Column="4"/>
<Grid/>
This is the code where I am setting the listbox ItemSource:
lbCatalogues.ItemsSource = catalogueViewModel.Records;
My question is how can I get the view to load correctly within the listbox so that each item in the listbox has a DataContext linked to that listbox Itemsource?
You already know the answer: simply remove <UserControl.DataContext> from your UserControl
You are telling your UserControl to use a new instance of CatelogueModel for the DataContext, and this overrides any DataContext that is set when you use your UserControl. See MSDN's list of Dependency Property Precedence for more info
I do not ever recommend setting the DataContext inside a UserControl. It goes against how WPF is meant to work by having separate UI and data layers, and is a problem for anyone trying to use your UserControl
As for your question about each item in the ListBox linking to the ItemsSource, DataTemplates simply tell WPF how to draw an object. The data behind the object still remains.
For example, your ListBox contains a list of Record objects, and your DataTemplate is telling the ListBox to draw each one of those records with CatelogueRowView. The actual data behind the CatelogRowView is still your data object from catelogueViewModel.Records
lbCatalogues.ItemsSource = catalogueViewModel.Records; instead of this Simply bind ItemsSource of ListBox as ItemsSource="{Binding Records}" in xaml. I hope this will help.

wrapping a data grid with a toolbox

I got a wpf application.
I want all my data grids in application to have a set of buttons above them.
Tried to use decorator and adorner without success(the dataGrid stopped showing rows)
Any suggestions?
Given that you're wanting to have functionality behind the toolbox buttons (which I assume will require a reference to the grid) it probably makes sense to inherit from a HeaderedContentControl for this. This does mean that you can put any content in the control, but it would be possible to put override the metadata to add validation for this.
Anywhere, here's the xaml:
<!-- ToolBoxGridControl.xaml -->
<HeaderedContentControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="WpfApplication3.ToolBoxGridControl">
<HeaderedContentControl.Header>
<StackPanel Orientation="Horizontal">
<Button/>
<Button/>
<Button/>
</StackPanel>
</HeaderedContentControl.Header>
<HeaderedContentControl.Template>
<ControlTemplate TargetType="HeaderedContentControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentControl Grid.Row="0" Content="{TemplateBinding Header}"/>
<ContentControl Grid.Row="1" Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</HeaderedContentControl.Template>
</HeaderedContentControl>
And the simple code-behind (where you can put your toolbox implementation).
public partial class ToolBoxGridControl : HeaderedContentControl
{
private DataGrid DataGrid { get { return (DataGrid)Content; } }
public ToolBoxGridControl()
{
this.InitializeComponent();
}
}
To actually use, you can just add the following to your XAML with your data grid
<local:ToolBoxGridControl>
<DataGrid/>
</local:ToolBoxGridControl>

Resources