If I have a resource loader that creates XAML on the fly (in this case a StackPanel is returned) how can I bind to this in the xaml?
My property is:
public StackPanel InfoPane {get;set;}
I have tried this but it doesnt work
<Grid>
<ContentPresenter Content="{Binding InfoPane}" />
</Grid>
(Sorry, too long for a comment.)
You approach is correct, the following minimal example works for me:
XAML:
<Grid>
<ContentPresenter Content="{Binding InfoPane}" />
</Grid>
Codebehind:
public StackPanel InfoPane { get; set; }
public MainWindow()
{
InitializeComponent();
InfoPane = new StackPanel();
InfoPane.Children.Add(new TextBlock { Text = "dynamically created" });
this.DataContext = this;
}
So, your problem must lie somewhere else. Maybe you set your InfoPane after setting the DataContext property of your window? Note that you do not invoke PropertyChanged, so the UI has no way of knowing that InfoPane has changed.
You do not need binding, all you need to do is add the StackPanel to the Children collection when the StackPanel has been created.
That said, my advise is to take a different route:
Put a content control in the Grid and bind its Content to actual content (not a control) and register for each type of content a ContentTemplate so the ContentControl will select the correct template depending on the content being displayed.
Creating UI in code should be avoided as much as possible because it is hard to maintain and WPF has ways to handle content dependent UI.
Related
In a WPF window, I want to create a placeholder and during the runtime I would like to render or load a UI element in it.
A placeholder in a window looks something like,
<Grid>
<Placeholder1/>
</Grid>
during the runtime, I would like an IconImage to be rendered there.
<Placeholder1>
<Image Source="pack//...." Loaded="Icon_Loaded" /> // Static source
</Placeholder1>
I wish to render the placeholder with the Image during runtime everytime I change Image's property, which should eventually trigger the Icon_Loaded event. I dont want to render the Image(Placeholder) on the startup, but later on.
Background story as to why I wish for a placeholder solution is,
in the below part of XAML part, I wish to trigger the Loaded event handler everytime I change the IconPack's binding property. So as a solution I reckoned, if I render the IconPack element in a placeholder during the runtime everytime I change its binding property, I get to trigger the Loaded event handler for every binding property change.
<IconPack:PackIconFontAwesome Grid.Column="2" Kind="{Binding cbCheckButtonCaptionIconType}"
Foreground="{Binding cbCheckButtonCaptionIconColor}"
Visibility="{Binding cbCheckButtonCaptionIsVisible}"
Loaded="PackIconFontAwesome_Loaded"/>
Could you please show me how to use ContentControl to do the same.
(I tried using TargetUpdated, but it doesnt help in my scenario, so the only option I have is Loaded)
It really depends on what you are trying to do. If your are always using images, use the Image control and bind the source to a property on your view model that contains the path.
private string _imagePath;
public string ImagePath
{
get => _imagePath;
set
{
if (_imagePath == value)
return;
_imagePath = value;
OnPropertyChanged(nameof(ImagePath));
}
}
<Grid>
<Image Source="{Binding ImagePath, TargetNullValue={x:Null}}" />
</Grid>
For more complex content, usually you would use a ContentControl. It has a Content property that you can assign any controls or bind to a property on a view model. It can be used with data templates (if you are not using data templates, you could also use other controls as placeholder, e.g. a Border.).
If you are directly assigning a control to the placeholder in code-behind, it could look like this.
<Grid>
<ContentControl x:Name="Placeholder" />
</Grid>
Placeholder.Content = new Image(...) {...};
If you want to bind a property on a view model and leverage data templating, you could create types:
public class ImageItem
{
public ImageItem(string path)
{
Path = path;
}
public string Path { get; }
}
ImagePath = ...;
Expose a property for the content item in your view model and assign it, e.g.:
public object ContentItem
{
get => _contentItem;
set
{
if (_contentItem == value)
return;
_contentItem = value;
OnPropertyChanged();
}
}
ContentItem = new ImageItem(...);
Then you could create a DataTemplate for this concrete type in XAML.
<DataTemplate DataType="{x:Type local:ImageItem}">
<Image Source="{Binding Path, TargetNullValue={x:Null}}"/>
</DataTemplate>
The resources must be in scope of the ContentControl, then it will automatically select the appropriate data template for the type bound to the Content property.
<Grid>
<ContentControl Content="{Binding ContentItem}" />
</Grid>
As you want to change the content at runtime, please be aware that it is essential to implement INotifyPropertyChanged when binding to properties, otherwise changes to properties will not be reflected in the user interface.
I am very sorry that this question is very basic. I just learned WPF and I failed to make simple two way binding to textbox.text to string property.
XAML Code:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="StuInfo">
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="10,26,0,0" TextWrapping="Wrap" Text="{Binding Path=str,Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
<Button x:Name="button" Content="Check" HorizontalAlignment="Left" Margin="10,67,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
</Grid>
C# Code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
str = "OK";
}
public string str { get; set; }
private void button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine(str);
}
}
First, the textbox does not show "OK", but it is blank. Then, I typed a different text into the textbox, for ex:"blablabla" without the quotes. Then I click the button to check if my str property has been updated. Apparently, str still contains "OK".
What did I do wrong here? What did I miss to make the binding work?
As a newcomer to WPF, all this Binding and DataContext jazz can be quite confusing. Let's start with your binding expression first...
<TextBox Text="{Binding Path=str, Mode=TwoWay}"/>
What this is saying is that you want to bind your Text property to whatever the DataContext of the TextBox is. DataContext is essentially the "thing" your TextBox is getting it's data from. Now here's the rub. DataContext is inherited from the element "above" it in the visual tree if not explicitly set. In your code, TextBox inherits it's DataContext from the Grid element, which in turn inherits it's DataContext from the Window element. Seeing that DataContext is not set in your Window the default value of the DataContext property will be applied, which is null. The DataContext is also not set in any of the child elements of your window, which, via inheritance, will set the DataContext of all children of that window to null.
It is important to note that you've left out the Source property in your binding expression.
<TextBox Text="{Binding Source=left_out, Path=str, Mode=TwoWay}"/>
When this property is left out, the binding's source is implied to be the elements DataContext, which in this case is null, for the reasons mentioned above. Basically, what your expression is saying here is that you want to bind your text property to DataContext.str which resolved by WPF is null.str.
OK, cool. Now, how do we set the DataContext of your TextBox.Text binding to the Code Behind for the window so we can get at that str property? There are several ways to do this, but for our purposes we'll focus on setting it explicitly in the binding of the TextBox.Text property. Now, there are three different "source" type properties of bindings. "Source" being where we want our control/element's binding to get it's data from. We have Source, RelativeSource, and ElementName. We're only going to focus on ElementName here, but the others are essential to research and understand.
So, let's name our Window element so we can access it through the ElementName property.
<Window x:Class="WpfApplication1.MainWindow"
x:Name="_window"
...
Now we can set the ElementName property on the TextBox.Text binding to refer to the window.
<TextBox Text="{Binding ElementName=_window, Path=str, Mode=TwoWay}"/>
This means the binding will look for the _window.str property when trying to resolve it's binding. At this point, you still probably won't see your str value reflected in the TextBox. This is because it's value is set after the InitializeComponent method in the window's constructor. This function is where bindings are resolved for the first time. If you were to set the value of str before calling InitializeComponent, you would see the value reflected in the TextBox.
This brings us to Dependency Properties. For now, just know that Dependency Properties have built in change notification, which your binding needs so it "knows" when the binding has changed and when to resolve the binding value again. Yes, you could use INotifyPropertyChanged in your code behind, but there are good arguments for using DependencyProperties in this case, which will only confuse the issue at this point. But, it is another one of those things that is essential to understand.
Here is the code for a DependencyProperty for your str property.
public static readonly DependencyProperty StrProperty
= DependencyProperty.Register("Str", typeof(string), typeof(MainWindow),
new FrameworkPropertyMetadata(FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Str
{
get{return (string)GetValue(StrProperty);}
set{SetValue(StrProperty,value);}
}
Now you'll be able to set the value like such and have it reflect through the binding to your TextBox.
public MainWindow()
{
InitializeComponent();
Str = "OK";
}
At this point, all should be well. I hope this helps out. It took me a while get the hang of WPF. My suggestion would be to read as much as you can on DataContext, Binding, and DependencyProperty as these are the core of WPF. Good luck!
The problem is that, you dont bind to codebehind of Window, but to DataContext.
Try this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new DC();
}
public class DC
{
public string str { get; set; }
public DC()
{
str = "OK";
}
}
}
Normally, you would have two different files, but for test, you can do it in one file.
After that, your DC (DataContext) should implement INotifyPropertyChanged interface.
Try to find some article about MVVM like this http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
I have a UserControl who's DataContext is being set to an instance of a ViewModel (using MVVM). But, I have controls within the UserControl which need to be bound to properties that only pertain to the view (which is why I placed them in code behind). I'm not sure how to bind this in xaml appropriately:
Note: SelectedOrderType is a property on the View-Model, and OrderTypes is a property on the UserControl itself.
<UserControl x:Class="MyNamespace.OrderControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="OrderUserControl">
<Grid>
...
<ComboBox ItemsSource="{Binding Path=OrderTypes, ElementName=OrderUserControl}"
SelectedValue="{Binding Path=SelectedOrderType}"
SelectedValuePath="OrderTypeCode"
DisplayMemberPath="OrderTypeName" />
</Grid>
</UserControl>
public partial class OrderControl : UserControl
{
public OrderControl()
{
InitializeComponent();
OrderTypes = ...;
}
public IReadOnlyCollection<OrderTypeInfo> OrderTypes { get; private set; }
}
Also, I know I can simply create a property on the View-Model, and I get that some people would suggest that that would be the correct place to put it... but I really would like to know how I could do what I'm attempting to do if not for this scenario, maybe for other scenarios in the future?
I may be wrong but would you not need to make a dependency property on your user control for "SelectedOrderType" and bind the the View Model to that property not bind directly to the view model from the user control.
That way your UserControl is not dependent on the view model?
Edit:
I think you could set it up the way you have it, but the binding for SelectedOrderType would need to be something like {Binding Path=DataContext.SelectedOrderType, ElementName=OrderUserControl}
I have a UserControl named "WorkspaceView" and its only purpose is to show other views as tabs. Call these views ViewA, ViewB etc. Which of these views to present should be determined on runtime, so I figured I needed a control that can present ... well ..stuff.
ContentControl to the rescue. Except ... I can't make it work. I'm trying to new up a usercontrol of type ViewA in the code behind and assign it to my MyContent, which is the ContentControl. I've tried:
public WorkspaceView()
{
InitializeComponent();
DataContext = new View(A); //Hoping that the DataContext will propagate down
}
Second attempt was
public WorkspaceView()
{
InitializeComponent();
var binding = new Binding {Source = new ViewA()};
MyContent.SetBinding(ContentControl.ContentProperty, binding);
}
In both cases, I see an empty box, but since I've hard wired a TextBlock into ViewA, I'd expect it to show me that text. What am I doing wrong?
Despite knowing that MVVM is the preferred way to develop WPF applications, I'd prefer to see how I can do this with code behind files. Later on, I will redo the application with MVVM, but first I need to get some basic understanding of WPF.
In response to the suggestions so far, I've tried
MyContent.Content = new ViewA();
but still I the text that is in ViewA does not appear. I've also at the bottom of this post included a screenshot of what the application renders.
WorkspaceView
Resource file
What is rendered
Have you tried simply doing this?
MyContent.Content = new ViewA();
EDIT
Try simplifying your code a bit and working from there. For instance:
public WorkspaceView()
{
InitializeComponent();
// Something better than UserControl should be used here
ObservableCollection<UserControl> views = new ObservableCollection<UserControl>();
views.Add(new ViewA());
views.Add(new ViewB());
DataContext = views;
}
<Border ..>
<TabControl x:Name="TabControl"
..
ItemsSource="{Binding}" />
</Border>
This code sets a WorkspaceView.DataContext to a collection of UserControls. When you specify {Binding} whithin WorkspaceView's XAML you are refering to the whole DataContext object (i.e. your collection.) This way you are setting the TabControl.ItemsSource to your collection of views.
Now you could create DataTemplates targeting the type of each view to control how each control is displayed in its tab within the TabControl.
I have a GridView that has several buttons. One of them is defined by the following template:
<DataTemplate x:Name="SubjectItemTemplate">
<Canvas Width="340" Height="170" VerticalAlignment="Top">
<Controls:ThreeImageButton HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,0,0,0"
NormalStateImageSource="{Binding NormalImage}"
HoverStateImageSource="{Binding HoverImage}"
PressedStateImageSource="{Binding PressedImage}" Command="{Binding Path=NavigateToUnitsPage}"
CommandParameter="{Binding}" Canvas.Left="0" Canvas.Top="0">
</Controls:ThreeImageButton>
</Canvas>
</DataTemplate>
Now I have a custom control as you can see, called ThreeImageButton. The button works fine when I use it on its own. But when I have it in the DataTemplate it won't bind properties to the code behind.
Right now, I have
x:Name="MyThreeImageButton"
in the custom button definition. And I connect to the code-behind like this:
<TextBlock Text="{Binding ElementName=MyThreeImageButton, Path=NormalStateImageSource}"/>
(This is just a test to display the text, in the actual code I would assign an image source to another property that is referred to by an element).
Right now, nothing is displayed in the TextBlock. What is the correct binding syntax I'm supposed to use to reach my properties?
Thanks!
Edit: I am setting the variable in the InitializeComponent function and I am using SetValue on the DependencyProperty.
Edit: Let me add the following information to be more clear
Scenario I:
In DataTemplate for GridView:
<UserControl CustomParameter="Literal Text">
In UserControl:
<TextBlock Text="{Binding CustomParameter}">
in UserControl .cs: this.DataContext = this
works!
Scenario II:
In DataTemplate for GridView:
<UserControl CustomParameter="{Binding ValueFromDataItem">
In UserControl:
<TextBlock Text="{Binding CustomParameter}">
in UserControl .cs: this.DataContext = this
nope!
I see,
So setting up a two-way binding to a custom property in a user control can be tricky because a user control cannot bind to a CLR property. Not only that but setting the data context on a user control has an unexpected effect on the binding inside it.
You can solve these problems with a little slight of code. Basically back your CLR properties with dependency properties and set the data context on a child element instead of the root user control.
Take a look at this sample. Let's pretend you have the following MainPage. That MainPage will eventually use our custom user control. So let's set the stage.
Here's the code-behind:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.DataContext = new /* your view model */
{
Title = Guid.NewGuid().ToString(),
};
}
}
In the code above I am simulating a complex view model with a simple anonymous class. It would be silly for you to implement your own like this, but at the same time it is silly for me to build a simple sample with the complete scaffolding. I bring this up only so it does not confuse you - as it could look like I am suggesting this approach in prod.
Here's the XAML:
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<local:MyUserControl Text="{Binding Title}" />
</Grid>
In the XAML above, there is absolutely nothing special. I already have reference to the user control in the local namespace and I simply declare it here.
Okay, now that we have a consumer of the control, it's worth pointing out that in testing developers can mistakenly think that their binding is working because they test with literal values. Literal values bind fine. It's binding from the underlying view model that hick-ups.
Let's say another thing, some developers tend to avoid dependency properties because the require a little more typing. People remember that [kbd]propdp[/kbd] is a handy Visual Studio snippet that stubs out a dependency property for you.
Take a look at this user control. It has two controls, a TextBox and a TextBlock which are there to demonstrate the OneWay and TwoWay functionality of this binding approach. We also implement INotifyPropertyChanged on the user control. For the most part, adding a view model in the case of a user control is overkill because the user control already acts like a view model. It's up to the developer, but it seems dumb to me.
Here's the code behind:
public sealed partial class MyUserControl : UserControl, INotifyPropertyChanged
{
public MyUserControl()
{
this.InitializeComponent();
}
// text property
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValueDp(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyUserControl), null);
// bindable
public event PropertyChangedEventHandler PropertyChanged;
void SetValueDp(DependencyProperty property, object value,
[System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
SetValue(property, value);
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
In the ode above, I have create a "Text" property and backed it with a dependency property. For a matter of reuse I have also implemented SetValueDp() which could be used again and again if I had more than a single property. Even though this demo has but one, I wanted to include this because the repetitive logic should certainly be abstracted out like this.
Here's the XAML:
<Grid Background="Black" DataContext="{Binding ElementName=userControl}">
<StackPanel>
<TextBox Text="{Binding Text, Mode=TwoWay}"
MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
<TextBlock Text="{Binding Text}"
MinHeight="100" Padding="15" FontWeight="Light" FontSize="50" />
</StackPanel>
</Grid>
In the XAML above, I do nothing special insofar as binding. The syntax simply binds to the Text property using the Mode appropriate to the control. Just like you would do normally. However, what's worth noticing is that the DataContext is NOT set on the user control. Instead, it is set on the Grid. As a point of fact, any control in the tree other than the user control could be used like this. Just don't set the data context of the user control.
That is it by the way.
I have tested it to make sure it works. Demonstrating both one and two way binding is pretty handy here. I might even turn this into a blog in case other developers want to find it and don't discover this question. Thanks for your question!
Best of luck!
As the comments alluded to, your DataTemplate is placing the datacontext of the items to whatever object you are adding to your list. This is not the same as the surrounding user control's data context. If you want to reference that datacontext's commands, do the following in the DataTemplate's bindings:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.NormalImage}
What this is saying is to go out and find the user control ancestor and use its datacontext and then look for the NormalImage property. If you run into problems, check your output window for binding errors. It is very helpful in finding binding problems.