WPF Binding not propagated - wpf

I have the following XAML:
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Background="#FFFFFFC7">
<Border VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="SkyBlue" BorderBrush="Black" BorderThickness="1" Width="50" >
<view:CActionListView DataContext="{Binding ActionViewModels.Inst}" />
</Border>
</ScrollViewer>
Where the ScollViewer and the Border are binded with an object of type "CustoWorkOrder.MainObject"
As you can see in the following picture; the binding is correct; and the Border binded object has a property called "ActionViewModels.Inst" (and the list of this object contains 3 items)
Click here for enlarged image.
The object CActionListView is binded with a "TRACAWPFLib.ViewModel.CActionListViewModel"; but it's not the one of the border; it's a new instance. (See following picture)
Click here for enlarged image.
Does somebody know why the binding is not propagated?

Related

WPF hyperlink in datatemplate listboxitem

I have a data template in WPF that is attached to a ResultsViewModel class and renders the VM in a table format. A bunch of these make up ListBoxItems in a ListBox. What I want is in each individual table to have a little X at the top right of the border where if you click it, it calls a function that removes that item from the listbox.
I have tried with a Hyperlink and event OnClick but then I have to have the DataTemplate in the main XAML and not in a resource dictionary as it needs a x:Class tag to use events, but then the event gets fired in the MainViewModel, which isn't the worst thing in the world as the observable list is held in the MainViewModel and needs to be removed at that point anyway, but I can't figure out how to get a reference to the ResultsViewModel of the list box item that contained the data template that was clicked
<DataTemplate x:Key="ErroredResultsTemplate" DataType="x:Type vm:ResultsViewModel" >
<Border x:Name="Border" BorderBrush="{StaticResource ResultProcessedBorder}" Background="{StaticResource ResultFill}" BorderThickness="4" CornerRadius="10" Margin="6" Padding="5" Width="110" Height="110">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="83" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Foreground="{StaticResource ResultGrayText}" FontWeight="Bold" HorizontalAlignment="Right" VerticalAlignment="Top">
<Hyperlink Click="Close_Results">X</Hyperlink>
</TextBlock>
<TextBlock Width="90" Text="An error occurred calculating results" TextWrapping="Wrap" Foreground="{StaticResource ResultGrayText}" FontWeight="Bold" Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Top" TextAlignment="Center" />
</Grid>
</Border>
</DataTemplate>
You can achieve this in two ways:
Create a property of type ResultsViewModel in your parent view model (that contains your collection of ResultsViewModel objects) and bind that to the SelectedItem property of your ListBox. Add some kind of RelayCommand to the parent view model to handle the delete action, add a Button to your DataTemplate and bind its Command property to the new command. Then, when any delete button is clicked, you can just remove the item found in the SelectedItem property from your collection and the UI should update accordingly (assuming that you've implemented the INotifyPropertyChange interface).
You can simply bind from the DataTemplate of each item in the ListBox to the parent view model directly. This assumes that you have a Command in your parent view model named Delete and that the parent view model is bound to the DataContext property of the Window or UserControl that the ListBox appears in. Also note the important CommandParameter="{Binding}" part which passes the data object from each item in the collection to the object parameter in the Command when a Command is called.
Example:
<Button Content="X" Command="{Binding DataContext.Delete,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type
XmlNameSpace:WindowOrUserControlName}}, Mode=OneWay}"
CommandParameter="{Binding}" />

Constraining a ListView's width to parent to force child text to wrap

I'm trying to get a window similar to a chat window, where a list of text items is drawn. The window should be resizable and each text item should wrap if it does not fit on one line.
What I have so far:
MessageItem - A user control, multiline TextBlock in a Border
<Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<Border BorderBrush="Silver" BorderThickness="1" Height="Auto" HorizontalAlignment="Left" Margin="0,10,0,10" Name="messageContainer" VerticalAlignment="Top" Width="Auto">
<TextBlock Height="Auto" HorizontalAlignment="Stretch" Margin="0" Name="messageContent" VerticalAlignment="Stretch" Width="Auto" Text="This is some longer text. Wow that wasn't as long as I thought." TextWrapping="Wrap" Padding="10" />
</Border>
</Grid>
MessageBox - A user control with a ListView that holds MessageItems
<Grid Name="messageGrid" VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<!--<StackPanel Height="Auto" HorizontalAlignment="Stretch" Margin="0" Name="messagePanel" VerticalAlignment="Stretch" Width="Auto">
</StackPanel> -->
<ListView HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch">
<local:MessageItem></local:MessageItem>
</ListView>
</ScrollViewer>
</Grid>
The Problem:
If I use the commented out StackPanel for holding MessageItems, It will shrink the MessageItem (and cause the text to wrap) correctly. If I use the ListView, it does not shrink.
I've more or less figured out why from research, but I haven't been able to figure out how to get around it. As far as I can tell I need to override MeasureOverride and/or ArrangeOverride, but I'm far too new to WPF to know WTF I'm doing. (rimshot)
I'm not sure why you're putting the ListView inside a ScrollViewer since the ListView has its own ScrollView internally.
In order to get your MessageItems to wrap you need to turn off any horizontal scrollbars, otherwise the container (ListView or ScrollViewer) will give the MessageItem as much space as it requires and show a scrollbar.
Try
<ScrollViewer ... ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
and
<ListView ... ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
although I'm not sure you even need the ScrollViewer.
You would probably be better off using a ListBox and an ItemTemplate instead of the ListView and user control

Cannot access drag adorner template

I've used the sample code provided by Bea Stollnitz (http://bea.stollnitz.com/blog/?p=53), in order to enable drag and drop in my application, and drag adorner, etc.
Everything works fine, my drag adorner is well displayed, I have all the behavior I want.
But (yes there is always a but), I cannot access the DataTemplate of the Drag Adorner, in order to display different data depending on the dragged data.
I have simplified the code, but the basics are still there.
This is the DataTemplate of my DragAdorner
<DataTemplate x:Key="DragAndDropTemplate" DataType="{x:Type MyType}">
<Grid>
<Grid Opacity="0.5">
<Border x:Name="HeaderBorder" CornerRadius="2" BorderThickness="1" Margin="5,2,5,2">
<Border x:Name="InsideBorder" CornerRadius="2" BorderThickness="1">
<TextBlock x:Name="number" Text="{Binding Name}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
</Border>
</Border>
</Grid>
<Border Width="17" Height="17" BorderBrush="White" HorizontalAlignment="Center" VerticalAlignment="Center" CornerRadius="1" x:Name="numberContainer" Visibility="Collapsed">
<TextBlock x:Name="number" Text="80" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White"/>
</Border>
</Grid>
</DataTemplate>
This is the code that create the Adorner :
if (this.draggedAdorner == null)
{
var adornerLayer = AdornerLayer.GetAdornerLayer(source);
this.draggedAdorner = new DraggedAdorner(draggedData, dataTemplate, source, adornerLayer);
}
And this is the code that init an adorner
public DraggedAdorner(List dragDropData, DataTemplate dragDropTemplate, FrameworkElement adornedElement, AdornerLayer adornerLayer)
: base(adornedElement)
{
this.adornerLayer = adornerLayer;
this.contentPresenter = new ContentPresenter();
this.contentPresenter.Content = dragDropData[0];
this.contentPresenter.ContentTemplate = dragDropTemplate;
this.adornerLayer.Add(this);
}
The draggedData, will be a list of MyType, I get the first item as the content of the ContentPresenter of my DraggedAdorner, so the DataTemplate can apply.
The problem is, I want to access the numberContainer and number control of the DataTemplate, in order to display the number of dragged object, in the adorner. But I cannot manage to access it, whatever I try, It ends with the "This operation is valid only on elements that have this template applied." message.
I have tought I could do something like this :
this.contentPresenter.ContentTemplate.FindName("number", this.contentPresenter);
Since the DataTemplate should apply to the ContentPresenter, but nope...
For information the adornedElement is the ListViewItem from which the drag occurs.
If you have any idea...
Ok, so I have found how to achieve what I wanted.
I don't know why it didn't comes to mind earlier, and why I didn't found anything about this before.
I have just added a single line before trying to access the template :
this.UpdateLayout()
Looks like it forces the ContentPresenter and DataTemplate object to be update and "re-rederend" so the ContentPresenter is really templated by my DataTemplate.

TextBox expanding with surrounding Grid but not with text

A window has a Grid with two columns. The left column contains a control with a constant width but with a height that adapts. The right column contains a TextBox that takes up all remaining space in the Grid (and thereby in the Window).
The Grid is given a minimal width and height and is wrapped within a ScrollViewer. If the user resizes the window to be smaller than the minimal width/height of the Grid, scrollbars are displayed.
This is exactly how I want it to be. However, a problem occurs when the user starts typing text. If the text is to long to fit in one line in the TextBox, I want the text to wrap. Therefore I set TextWrapping="Wrap" on the TextBox. But since the TextBox has an automatic width and is wrapped in a ScrollViewer (its actually the whole Grid that is wrapped), the TextBox just keeps expanding to the right.
I do want the TextBox to expand if the window is expanded, but I don't want the TextBox to expand by the text. Rather the text should wrap inside the available TextBox. If the text don't fit within the TextBox height, a scrollbar should be displayed within the TextBox.
Is there a way to accomplish this?
Below is some code that shows my problem:
<Window x:Class="AdaptingTextBoxes.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="300" Width="400" Background="DarkCyan">
<Grid Margin="10" Name="LayoutRoot">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid MinWidth="300" MinHeight="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Margin="0,0,10,0" Content="Button" Width="100" />
<TextBox Grid.Column="1" AcceptsReturn="True" TextWrapping="Wrap" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />
</Grid>
</ScrollViewer>
</Grid>
</Window>
You could use an invisible border (its hacky but it works - its how I tend to sort out dynamic textbox sizes in Xaml):
<Border BorderThickness="0" x:Name="border" Grid.Column="1" Margin="0.5" />
<TextBox Grid.Column="1" AcceptsReturn="True" TextWrapping="Wrap" Width="{Binding ActualWidth, ElementName=border}" Height="{Binding ActualHeight, ElementName=border}" />
Have you tried setting the MaxWidth property on just the TextBox?
Edit after OP's comment
I would try getting rid of the ScrollViewer. The sizing used in the Grid's layout should take care of re-sizing and the scroll bar settings on the TextBox should take care of the rest.
The answer is based on Leom's answer.
The solution works great when you enlarge the window, but the resizing is not smooth when you make the window smaller. As the textbox participates in the grid's layout, it has to perform layout process multiple times. You can fix that by putting the texbox in the canvas, so the change of the size of the textbox no longer triggers the grid's re-layout.
The updated code:
<Border BorderThickness="0" x:Name="border" Grid.Column="1" Margin="0.5" />
<Canvas Grid.Column="1">
<TextBox AcceptsReturn="True" TextWrapping="Wrap" Width="{Binding ActualWidth, ElementName=border}" Height="{Binding ActualHeight, ElementName=border}" />
</Canvas>

Difference between Control Template and DataTemplate in WPF

What is difference between a ControlTemplate and a DataTemplate in WPF?
Typically a control is rendered for its own sake, and doesn't reflect underlying data. For example, a Button wouldn't be bound to a business object - it's there purely so it can be clicked on. A ContentControl or ListBox, however, generally appear so that they can present data for the user.
A DataTemplate, therefore, is used to provide visual structure for underlying data, while a ControlTemplate has nothing to do with underlying data and simply provides visual layout for the control itself.
A ControlTemplate will generally only contain TemplateBinding expressions, binding back to the properties on the control itself, while a DataTemplate will contain standard Binding expressions, binding to the properties of its DataContext (the business/domain object or view model).
Very basically a ControlTemplate describes how to display a Control while a DataTemplate describes how to display Data.
For example:
A Label is a control and will include a ControlTemplate which says the Label should be displayed using a Border around some Content (a DataTemplate or another Control).
A Customer class is Data and will be displayed using a DataTemplate which could say to display the Customer type as a StackPanel containing two TextBlocks one showing the Name and the other displaying the phone number. It might be helpful to note that all classes are displayed using DataTemplates, you will just usually use the default template which is a TextBlock with the Text property set to the result of the Object's ToString method.
Troels Larsen has a good explanation on MSDN forum
<Window x:Class="WpfApplication7.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">
<Window.Resources>
<DataTemplate x:Key="ButtonContentTemplate">
<StackPanel Orientation="Horizontal">
<Grid Height="8" Width="8">
<Path HorizontalAlignment="Stretch"
Margin="0,0,1.8,1.8"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FF000000"
Data="M0.5,5.7 L0.5,0.5 L5.7,0.5"/>
<Path HorizontalAlignment="Stretch"
Margin="2,3,0,0"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FFFFFFFF"
Data="M3.2,7.5 L7.5,7.5 L7.5,3.5"/>
<Path HorizontalAlignment="Stretch"
Margin="1.2,1.4,0.7,0.7"
VerticalAlignment="Stretch" Fill="#FFFFFFFF" Stretch="Fill" Stroke="#FF000000"
Data="M2.5,2.5 L7.5,7.5"/>
<Path HorizontalAlignment="Stretch"
Margin="1.7,2.0,1,1"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FF000000"
Data="M3,7.5 L7.5,7.5 L7.5,3.5"/>
<Path HorizontalAlignment="Stretch"
Margin="1,1,1,1"
VerticalAlignment="Stretch" Stretch="Fill" Stroke="#FFFFFFFF"
Data="M1.5,6.5 L1.5,1 L6.5,1.5"/>
</Grid>
<ContentPresenter Content="{Binding}"/>
</StackPanel>
</DataTemplate>
<ControlTemplate TargetType="Button" x:Key="ButtonControlTemplate">
<Grid>
<Ellipse Fill="{TemplateBinding Background}"/>
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Window.Resources>
<StackPanel>
<Button Template="{StaticResource ButtonControlTemplate}" ContentTemplate="{StaticResource ButtonContentTemplate}" Content="1"/>
<Button Template="{StaticResource ButtonControlTemplate}" ContentTemplate="{StaticResource ButtonContentTemplate}" Content="2"/>
<Button Template="{StaticResource ButtonControlTemplate}" ContentTemplate="{StaticResource ButtonContentTemplate}" Content="3"/>
</StackPanel>
</Window>
(Templates blatently stolen from
http://msdn.microsoft.com/en-us/library/system.windows.controls.controltemplate.aspx
and
http://msdn.microsoft.com/en-us/library/system.windows.controls.contentcontrol.contenttemplate%28VS.95%29.aspx
respectively)
Anyway, the ControlTemplate decides how the Button itself looks, while
the ContentTemplate decides how the Content of the button looks. So
you could bind the content to one of you data classes and have it
present itself however you wanted it.
ControlTemplate: Represents control style.
DataTemplate: Represents data style(How would you like to show your data).
All controls are using default control template that you can override through template property.
For example
Button template is a control template.
Button content template is a data template
<Button VerticalAlignment="Top" >
<Button.Template>
<ControlTemplate >
<Grid>
<Rectangle Fill="Blue" RadiusX="20" RadiusY="20"/>
<Ellipse Fill="Red" />
<ContentPresenter Content="{Binding}">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="50">
<TextBlock Text="Name" Margin="5"/>
<TextBox Text="{Binding UserName, Mode=TwoWay}" Margin="5" Width="100"/>
<Button Content="Show Name" Click="OnClickShowName" />
</StackPanel>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
</Grid>
</ControlTemplate>
</Button.Template>
</Button>
public String UserName
{
get { return userName; }
set
{
userName = value;
this.NotifyPropertyChanged("UserName");
}
}
ControlTemplate - Changing the appearance of element. For example Button can contain image and text
DataTemplate - Representing the underlying data using the elements.
ControlTemplate DEFINES the visual appearance, DataTemplate REPLACES the visual appearance of a data item.
Example: I want to show a button from rectangular to circle form => Control Template.
And if you have complex objects to the control, it just calls and shows ToString(), with DataTemplate you can get various members and display and change their values of the data object.
All of the above answers are great but there is a key difference that was missed. That helps make better decisions about when to use what. It is ItemTemplate property:
DataTemplate is used for elements that provide ItemTemplate property for you to replace its items' content using DataTemplates you define previously according to bound data through a selector that you provide.
But if your control does not provide this luxury for you then you still can use a ContentView that can display its content from predefined ControlTemplate. Interestingly, you can change the ControlTemplate property of your ContentView at runtime. One more thing to note that unlike controls with ItemTemplate property, you cannot have a TemplateSelector for this (ContentView) control. However, you still can create triggers to change the ControlTemplate at runtime.

Resources