Binding dynamically generated textblock WPF MVVM Light - wpf

I am using MVVMM Light WPF and I want to do the following: Generate textboxes dynamically and bind them to a property of a class.
I already have the following but it doesn't show up in my view when running the application.
This is my collection:
private ObservableCollection<Border> _controllekes;
public ObservableCollection<Border> Controllekes
{
get { return _controllekes; }
set
{
_controllekes = value;
RaisePropertyChanged("Controllekes");
}
}
This it my xaml:
<ItemsControl ItemsSource="{Binding Path=Controllekes}">
</ItemsControl>
This is a part where I fill the itemsource "Controllekes":
Controllekes = new ObservableCollection<Border>();
Border border = new Border();
border.BorderThickness = new System.Windows.Thickness(5);
border.BorderBrush = Brushes.AliceBlue;
border.Padding = new System.Windows.Thickness(5);
TextBlock tb = new TextBlock();
tb.Background = Brushes.Red;
Binding nameTextBinding = new Binding("Controllekes");
nameTextBinding.Path = new System.Windows.PropertyPath(this.Dossier.Omschrijving);
nameTextBinding.Mode = BindingMode.OneWay;
//nameTextBinding.Source = this.Dossier.Omschrijving;
tb.SetBinding(TextBlock.TextProperty, nameTextBinding);
border.Child = tb;
this.Controllekes.Add(border);
What it does it creates a border with in this border a textblock where the binding should happen. I whish to bind the property this.Dossier.Omschrijving (Dossier is the class). If I just enter a string in the textbox it works.
In runtime the border gets generated but the textblock remains empty. The object Dossier.Omschrijving contains information.
What do I do wrong?
EDIT:
safe put me in the right direction and the answer of ItemsControl with multiple DataTemplates for a viewmodel made me finish the job :)

through all that and use ItemTemplate
<ItemsControl x:Name="ic">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox IsChecked="{Binding yourboolProperty}"/>
<TextBlock Background="Red" Text="{Binding yourStringProperty}"></TextBlock>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and set ic's ItemsSource to List

Related

Performance issues with CellTemplate of GridViewColumn

I am using a ListView with it's View set to GridView to display items in dynamically generated columns. The ListView's virtualization is enabled. For this example I want to display up to 10 rows with 50 columns (but the count of columns is dynamic).
The first thing I do is to create the columns in code behind in a loop.
"Items" is just an ObservableCollection of strings in my view model for one row.
int index = 0;
foreach(Header header in Headers)
{
GridViewColumn column = new GridViewColumn();
column.Header = // the view model of the header;
column.HeaderTemplate = Resources["HeaderTemplate"] as DataTemplate;
column.DisplayMemberBinding = new Binding("Items" + "[" + index + "]");
m_MyGridView.Columns.Add(column);
index++;
}
Now, when I change the ItemsSource of my ListView, the data gets updated quiet fast.
Unfortunately, each string in every cell has to be formatted (font color, foreground etc). So I cannot use DisplayMemberBinding, but CellTemplate to assign a DataTemplate to every cell.
For this purpose I create a DataTemplate in xaml in my control that holds the ListView. This contains just a TextBlock with some bindings to properties of the cell's view model to define the string style of the TextBlock.
Now, Items is not just a collection of string but a collection of CellViewModels that not only contain the value to display but also information of how to display it. This information is then bound inside the DataTemplate.
The CellTemplate is set up in code and assigned to each GridViewColumn's CellTemplate:
foreach(Header header in Headers)
{
GridViewColumn column = new GridViewColumn();
column.Header = // the view model of the header;
column.HeaderTemplate = Resources["HeaderTemplate"] as DataTemplate;
column.CellTemplate = createCellTemplate(index);
m_MyGridView.Columns.Add(column);
index++;
}
DataTemplate getCellTemplate(int index)
{
FrameworkElementFactory elementFactory = new FrameworkElementFactory (typeof (ContentControl));
elementFactory.SetBinding(ContentControl.ContentProperty, new Binding("Items" + "[" + index + "]"));
DataTemplate dt = Resources["ValueTemplate"] as DataTemplate;
elementFactory.SetValue(ContentControl.ContentTemplateProperty, dt);
DataTemplate dataTemplate = new DataTemplate ();
dataTemplate.VisualTree = elementFactory;
return dataTemplate;
}
When I do this, things get really slow when I'm changing the ItemsSource of the ListView. It now takes nearly a second to render the new cells each time I change the ItemsSource to display different data.
So is there a way to speed things up? I thought about having the formatted string already in my CellViewModel by using a Paragraph, but unfortunately DisplayMemberBinding only supports string, so I have to use the CellTemplate.
Edit: so, here is the listview i'm using:
<ListView ItemsSource="{Binding ElementName=Control, Path=ItemViewModels}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" VirtualizingStackPanel.IsVirtualizing="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.View>
<GridView x:Name="m_MyGridView"/>
</ListView.View>
</ListView>
and the DataTemplate for the cells:
<DataTemplate x:Key="ValueTemplate">
<Border Background="{Binding Path=BackgroundColor">
<TextBlock Text="{Binding Path=Value}"
Foreground="{Binding Path=ForegroundColor}"
FontStyle="{Binding Path=FontStyle}"
FontWeight="{Binding Path=FontWeight}"
</TextBlock>
</Border>
</DataTemplate>
The function getCellTemplate is called only once when the headers are created. When the ItemsSource changes, it is not called, because they have been created already.

WPF Bind textbox Text to another control property [duplicate]

I have a situation where I need to create View box with one button. The xaml for this is as below: Please observe Width property of viewbox. The Width should be increased/decreased according to a slider bar(moving to right increases it, to left decreases it). As listed below I know how to do it in xaml and it works fine. But my requirement is to be able to create viewbox in code behind and assign it the properties.
<WrapPanel x:Name="_wrpImageButtons" Grid.IsSharedSizeScope="True"
ScrollViewer.CanContentScroll="True" d:LayoutOverrides="Height"
Margin="5">
<Viewbox x:Name="_ScaleButton"
Width="{Binding Value, ElementName=ZoomSlider}" Stretch="Fill">
<CustomButton:_uscVCARSImagesButton x:Name="_btnImage1"/>
</Viewbox>
</WrapPanel>
Thanks.
This should do what you want:
Viewbox x = new Viewbox();
Binding bnd = new Binding("Value") { ElementName = "ZoomSlider"};
BindingOperations.SetBinding(x, Viewbox.WidthProperty, bnd);
// ... Code to insert the Viewbox into the WrapPanel etc.
You can create the binding relatively easily in Code Behind:
var widthBinding = new Binding("Value") { ElementName = "ZoomSlider" };
_ScaleButton.SetBinding(FrameworkElement.WidthProperty, widthBinding);

Changing content changes DataContext when using DataTemplate

X problem.
I want to enlarge (let it take whole available space) a part of window content temporarily.
Window layout is pretty complicated: many nested panels, splitters, content to enlarge is 10 levels deep. Changing Visibility to stretch content is simply not enough (thanks to splitters) and seems very complicated.
Y problem
I decide to move that content into a user control and do something like (pseudo-code)
if(IsEnlarged)
{
oldContent = window.Content; // store
window.Content = newContent;
}
else
window.Content = oldContent; // restore
No problems. It was working perfectly in test project ... until I start using data templates.
Problem: if data templates are used then as soon as window.Content = newContent occurs, then that newContent.DataContext is lost and become the same as window.DataContext. This will trigger various binding errors, attached behaviors suddenly changes to default value, etc.. all kind of bad stuff.
Question: why DataContext is changing? How to prevent/fix this issue?
Here is a repro (sorry, can't make it any shorter):
MainWindow.xaml contains
<Window.Resources>
<DataTemplate DataType="{x:Type local:ViewModel}">
<local:UserControl1 />
</DataTemplate>
</Window.Resources>
<Grid Background="Gray">
<ContentControl Content="{Binding ViewModel}" />
</Grid>
MainWindow.cs contains
public ViewModel ViewModel { get; } = new ViewModel();
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
UserControl1.xaml contains
<Button Width="100"
Height="100"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
Command="{Binding Command}" />
ViewModel (using DelegateCommand)
public class ViewModel
{
public DelegateCommand Command { get; set; }
bool _set;
object _content;
public ViewModel()
{
Command = new DelegateCommand(o =>
{
var button = (Button)o;
var window = Window.GetWindow(button);
_set = !_set;
if (_set)
{
_content = window.Content;
var a = button.DataContext; // a == ViewModel
window.Content = button;
var b = button.DataContext; // b == MainWindow ??? why???
}
else
window.Content = _content;
});
}
}
Set breakpoint on var a = ..., start program, click the button, do steps and observe button.DataContext as well as binding error in Output window.
ok here some general thoughts.
if you bind a object(viewmodel) to the Content property of the contentcontrol then wpf uses a DataTemplate to visualize the object. if you dont have a DataTemplate you just see object.ToString(). DataContext inheritance means that the DataContext is inherited to the child elements. so a real usercontrol will inherit the DataContext from the parent. the are common mistakes you find here on stackoverflow when creating UserControls - they often break DataContext inheritance and set the DataContext to self or a new DataContext.
UserControl DataContext Binding
You must be trying to use your DataTemplate as ContentTemplate for your ContentControl. As ContentTemplate operates on Content, so it will use Content as its DataContext. And your Content contains ViewModel.
Once your Button is no longer part of your DataTemplate, so it will use MainWindow's DataContext.
No seeing your comments, I am assuming that you want DataContext of UserControl to remain intact, even if your UserControl is not part of DataTemplate.
So, simple set DataContext of Button explicitly using XAML using RelativeSource.
Example,
<Button Content="{Binding Data}" DataContext="{Binding vm1, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}" />
Playing with DataContext in code is not a good idea.

Set Binding for DataTemplate's elements programmatically

I have such a template
<Window.Resources>
<DataTemplate x:Key="MemberCoefDataTemplate">
<StackPanel>
<CheckBox Name="CheckBox"></CheckBox>
<TextBox Name="TextBox"></TextBox>
</StackPanel>
</DataTemplate>
</Window.Resources>
And i use it in a grid
<DataGridTemplateColumn CellTemplate="{StaticResource MemberCoefDataTemplate}" />
I need to dynamically add columns to the grid. As Binding i use indexer property. So i need to set binding dynamically, because i don't know to which index bind to. When i just for test tried this
var column = new DataGridTemplateColumn();
column.CellTemplate = (DataTemplate)Application.Current.MainWindow.Resources["MemberCoefDataTemplate"];
TextBox tb = column.CellTemplate.FindName("TextBox", dg) as TextBox;
I got InvalidOperationException wit description:
this operation is valid only on elements that have this template applied
Usually to get the control during the setup time, you would have to call the method LoadContent();
I would therefore try column.CellTemplate.LoadContent().
You can find a full explanation about your problem (and a solution of course) written by Josh Smith here on his blog.
Indeed this is the point where the DataGridTemplateColumn applies the DataTemplate:
private FrameworkElement LoadTemplateContent(bool isEditing, object dataItem, DataGridCell cell)
{
DataTemplate dataTemplate;
DataTemplateSelector dataTemplateSelector;
this.ChooseCellTemplateAndSelector(isEditing, out dataTemplate, out dataTemplateSelector);
if (dataTemplate != null || dataTemplateSelector != null)
{
ContentPresenter contentPresenter = new ContentPresenter();
BindingOperations.SetBinding(contentPresenter, ContentPresenter.ContentProperty, new Binding());
contentPresenter.ContentTemplate = dataTemplate;
contentPresenter.ContentTemplateSelector = dataTemplateSelector;
return contentPresenter;
}
return null;
}
As you can see a ContentPresenter is used.

StoryBoard.Begin on ItemControl Items?

I have an ItemsControl, and its ItemsSource is an ObservableCollection. I also have a DataTemplate to display each MyPerson.
<ItemsControl Name="ic_People" ItemTemplate="{StaticResource dtPerson}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
The DataTemplate itself is a bit complex, but part of it is an Image.
In codebehind I'd like to animate some/all of these DataTemplates, by moving the Image (using Canvas.Top etc).
My codebehind got stuck here;
Storyboard sb = new Storyboard();
DoubleAnimation da = new DoubleAnimation();
da.From = 0;
da.By = 68;
da.Duration = new Duration(TimeSpan.FromSeconds(.2));
sb.Children.Add(da);
object prop = Canvas.TopProperty;
Storyboard.SetTargetProperty(da, new PropertyPath(prop));
sb.Begin(???, true); <---- needs a FrameworkElement
I have access to the ObservableCollection. How do I get access to DataTemplate or Person as a FrameworkElement (which is what the Begin method requires)?
thanks

Resources