I have two controls on WPF
<Button HorizontalAlignment="Center"
Name="btnChange"
Click="btnChange_Click"
Content="Click Me" />
<Label Name="lblCompanyId"
HorizontalAlignment="Center"
DataContext="{Binding ElementName=_this}"
Content="{Binding Path=CompanyName}" />
As we can see that label is bound to local property(in code Behind), I don't see any value on label when I click button...
Below is my code behind...
public static readonly DependencyProperty CompanyNameProperty =
DependencyProperty.Register("CompanyName", typeof(string), typeof(Window3), new UIPropertyMetadata(string.Empty));
public string CompanyName {
get { return (string)this.GetValue(CompanyNameProperty); }
set { this.SetValue(CompanyNameProperty, value); }
}
private void btnChange_Click(object sender, RoutedEventArgs e) {
this.CompanyName = "This is new company from code beind";
}
Try:
Content="{Binding ElementName=_this, Path=CompanyName}"
Without the DataContext binding.
EDIT
I have no problems with your code, have named your window to x:Name="_this"?
<Window x:Class="WpfStackOverflowSpielWiese.Window3"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window3"
Height="300"
Width="300"
x:Name="_this">
<Grid>
<StackPanel>
<Button HorizontalAlignment="Center"
Name="btnChange"
Click="btnChange_Click"
Content="Click Me" />
<Label Name="lblCompanyId"
HorizontalAlignment="Center"
DataContext="{Binding ElementName=_this}"
Content="{Binding Path=CompanyName}"></Label>
</StackPanel>
</Grid>
</Window>
Is your window really Window3?
public partial class Window3 : Window
{
public Window3() {
this.InitializeComponent();
}
public static readonly DependencyProperty CompanyNameProperty =
DependencyProperty.Register("CompanyName", typeof(string), typeof(Window3), new UIPropertyMetadata(string.Empty));
public string CompanyName {
get { return (string)this.GetValue(CompanyNameProperty); }
set { this.SetValue(CompanyNameProperty, value); }
}
private void btnChange_Click(object sender, RoutedEventArgs e) {
this.CompanyName = "This is new company from code behind";
}
}
You are currently binding your Label's DataContext to a Button, and then trying to set it's Content to CompanyName, however CompanyName is not a valid property on Button
Specify DataContext in your binding Path to bind to Button.DataContext.CompanyName instead of Button.CompanyName
Also, I'd recommend just binding the Content instead of binding both the DataContext and Content
<Label Content="{Binding ElementName=btnChange, Path=DataContext.CompanyName}" />
And if your code looks exactly like the code sample posted, then both the Button and Label have the same DataContext, so you can bind directly to CompanyName
<Label Content="{Binding CompanyName}" />
Edit
Just noticed that your Label's binding was to a control named _this. I had assumed it was the Button, although I see now that your Button's name is btnChange, not _this.
It doesn't matter though, the answer is still the same. You're trying to bind to a UI Control's CompanyName property, which is not a valid property.
Related
I'm trying to create a UserControl in my WPF project which I want should have a DependencyProperty that I can bind to in the parent. The project is written as MVVM and I'm using Caliburn micro.
I really want to write clean and maintainable code using MVVM, so I want my UserControls to utilize viewmodels as much as possible and code behind as little as possible.
The problem is that I'm unsuccessful in getting the binding between the parent and the UserControl viewmodel to work correctly.
MyUserControl:
public partial class MyUserControlView : UserControl
{
public MyUserControlView()
{
InitializeComponent();
// If no Datacontext is set, binding between parent property and textbox text works - one way only (set from parent)!.
// -
// If Datacontext is set to this, bindings with properties in MyUserControlView code behind works.
//DataContext = this;
// If Datacontext is set to MyUserControlViewModel, binding between MyUserControlViewModel and MyUserControlView works, but not with parent.
DataContext = new MyUserControlViewModel();
}
public string ProjectNumber
{
get { return (string)GetValue(MyUserControlValueProperty); }
set { SetValue(MyUserControlValueProperty, value); }
}
public static readonly DependencyProperty MyUserControlValueProperty =
DependencyProperty.Register("ProjectNumber", typeof(string), typeof(MyUserControlView), new PropertyMetadata(null, new PropertyChangedCallback(OnProjectNumberUpdate)));
private static void OnProjectNumberUpdate(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var view = d as MyUserControlView;
view.ProjectNumberText.Text = e.NewValue as string;
}
}
MyUserControl code behind:
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="In MyUserControl: " />
<TextBlock Text="{Binding ProjectNumber}" />
</StackPanel>
<TextBox Name="ProjectNumberText" Text="{Binding ProjectNumber, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</StackPanel>
MyUserControl ViewModel:
public class MyUserControlViewModel : Screen
{
private string _projectNumber;
public string ProjectNumber
{
get { return _projectNumber; }
set
{
_projectNumber = value;
NotifyOfPropertyChange(() => ProjectNumber);
}
}
}
Parent view:
<StackPanel>
<local:MyUserControlView ProjectNumber="{Binding ParentProjectNumber}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="In parent: "/>
<TextBlock Text="{Binding ParentProjectNumber}" />
</StackPanel>
</StackPanel>
Parent ViewModel:
public class ShellViewModel : Screen
{
public ShellViewModel()
{
ParentProjectNumber = "Hello from parent!";
}
private string _parentProjectNumber;
public string ParentProjectNumber
{
get { return _parentProjectNumber; }
set
{
_parentProjectNumber = value;
NotifyOfPropertyChange(() => ParentProjectNumber);
}
}
}
I know I'm probably way off here, but I have no idea what to do to get the bindings to work correctly.
Is there a better way to bind between a DependencyProperty and a viewmodel? Can I put the DP in the viewmodel somehow?
Here is the entire project solution: https://github.com/ottosson/DependencyPropertyTest
don't change UserControl.DataContext from inside UserControl. it can and will create issues later.
use proper name for DP (ProjectNumberProperty and corresponding ProjectNumber) and add BindsTwoWayByDefault to metadata:
public partial class MyUserControlView : UserControl
{
public MyUserControlView()
{
InitializeComponent();
}
public string ProjectNumber
{
get { return (string)GetValue(ProjectNumberProperty); }
set { SetValue(ProjectNumberProperty, value); }
}
public static readonly DependencyProperty ProjectNumberProperty = DependencyProperty.Register
(
"ProjectNumber",
typeof(string),
typeof(MyUserControlView),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)
);
}
fix bindings in xaml:
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="In MyUserControl: " />
<TextBlock Text="{Binding Path=ProjectNumber, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</StackPanel>
<TextBox Text="{Binding Path=ProjectNumber, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</StackPanel>
that should do it.
btw, "clean and maintainable code using MVVM" and "want my UserControls to utilize viewmodels as much as possible" sort of contradict each other.
also nothing wrong with code-behind in UserControls as long as that code handles only view functionality. for example: DataGrid source code contains 8000+ LoC
I have this Custom Control
XAML:
<UserControl x:Class="WpfApplication1.UC"
...
x:Name="uc">
<StackPanel HorizontalAlignment="Left" VerticalAlignment="Top" Orientation="Horizontal">
<TextBox Text="{Binding Test, ElementName=uc}" Width="50" HorizontalAlignment="Left"/>
</StackPanel>
</UserControl>
C#
public partial class UC : UserControl
{
public static readonly DependencyProperty TestProperty;
public string Test
{
get
{
return (string)GetValue(TestProperty);
}
set
{
SetValue(TestProperty, value);
}
}
static UC()
{
TestProperty = DependencyProperty.Register("Test",typeof(string),
typeof(UC), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
}
public UC()
{
InitializeComponent();
}
}
And this is how i used that custom control:
<DockPanel>
<ItemsControl ItemsSource="{Binding Path=DataList}"
DockPanel.Dock="Left">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding}" CommandParameter="{Binding}" Click="Button_Click"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<local:UC Test="{Binding SelectedString, Mode=OneWay}"/>
</DockPanel>
--
public partial class MainWindow : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
private ObservableCollection<string> _dataList;
public ObservableCollection<string> DataList
{
get { return _dataList; }
set
{
_dataList = value;
OnPropertyChanged("DataList");
}
}
private string _selectedString;
public string SelectedString
{
get { return _selectedString; }
set
{
_selectedString = value;
OnPropertyChanged("SelectedString");
}
}
public MainWindow()
{
InitializeComponent();
this.DataList = new ObservableCollection<string>();
this.DataList.Add("1111");
this.DataList.Add("2222");
this.DataList.Add("3333");
this.DataList.Add("4444");
this.DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.SelectedString = (sender as Button).CommandParameter.ToString();
}
}
If I do not change text of UC, everything is ok. When I click each button in the left panel, button's content is displayed on UC.
But when I change text of UC (ex: to 9999), Test property lost binding. When I click each button in the left panel, text of UC is the same that was changed (9999). In debug I see that SelectedString is changed by each button click but UC's text is not.
I can 'fix' this problem by using this <TextBox Text="{Binding Test, ElementName=uc, Mode=OneWay}" Width="50" HorizontalAlignment="Left"/> in the UC.
But I just want to understand the problem, can someone help me to explain it please.
Setting the value of the target of a OneWay binding clears the binding. The binding <TextBox Text="{Binding Test, ElementName=uc}" is two way, and when the text changes it updates the Test property as well. But the Test property is the Target of a OneWay binding, and that binding is cleared.
Your 'fix' works because as a OneWay binding, it never updates Test and the binding is never cleared. Depending on what you want, you could also change the UC binding to <local:UC Test="{Binding SelectedString, Mode=TwoWay}"/> Two Way bindings are not cleared when the source or target is updated through another method.
The issue is with below line
<local:UC Test="{Binding SelectedString, Mode=OneWay}"/>
The mode is set as oneway for SelectString binding so text will be updated when the value from code base changes. To change either the source property or the target property to automatically update the binding source as TwoWay.
<local:UC Test="{Binding SelectedString, Mode=TwoWay}"/>
I have a Window, containing a UserControl 'TemplateEditor'. The (shortened) TemplateEditor XAML is:
<UserControl x:Class="xxx.Windows.Core.Controls.TemplateEditor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
x:Name="userControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<TextBox Grid.Row="1" x:Name="textBox" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" />
</Grid>
</UserControl>
I want to be able to bind data through the TemplateEditor into the TextBox "textBox". I'm using a DependencyProperty to mask the TextBox in the code-behind:
namespace xxx.Windows.Core.Controls
{
public partial class TemplateEditor : UserControl
{
public string Text
{
get
{
string s=(string)GetValue(TextProperty);
return s;
}
set
{
if (Text != value)
{
SetValue(TextProperty, value);
}
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TemplateEditor),new PropertyMetadata(null,Text_PropertyChanged));
public TemplateEditor()
{
InitializeComponent();
}
private static void Text_PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((TemplateEditor)source).textBox.Text = (string)e.NewValue;
}
}
}
So from that you can see I have a DependencyProperty Text that I have a callback on to pick up changes made by to the binding (say, from the ViewModel) and apply to the TextBox value.
This works.
The problem is, I can't seem to have the binding work in reverse, ie. to get the value back out of the Text property (and therefore the TextBox) and back in to the binding consumer (the ViewModel). I've debugged calling GetValue(TextProperty) and this returns the correct value so the DP dictionary is correctly updating.
Given the following Binding in the parent Window's XAML:
<src:ApplicationWindowBase x:Class="xxx.Windows.Client.Filing"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:src="clr-namespace:xxx.Windows.Core;assembly=MIGTurbo1.Windows.Core"
xmlns:viewmodel="clr-namespace:xxx.Windows.Client.ViewModel"
xmlns:converters="clr-namespace:xxx.Windows.Client.Converters"
xmlns:xxx_Windows_Core_Controls="clr-namespace:xxx.Windows.Core.Controls;assembly=xxx.Windows.Core"
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"
mc:Ignorable="d"
Title="File Item(s)" Height="450" Width="550" ShowInTaskbar="False">
<src:ApplicationWindowBase.Resources>
<viewmodel:ViewModelLocator x:Key="ViewModelLocator" d:IsDataSource="True"/>
<converters:FileableItemNameStringConverter x:Key="fileableItemNameStringConverter" />
<converters:FileableItemTypeStringConverter x:Key="fileableItemTypeStringConverter" />
<converters:FileableItemMetaDataStringConverter x:Key="fileableItemMetaDataStringConverter" />
<converters:FileableItemIconConverter x:Key="fileableItemIconConverter" />
</src:ApplicationWindowBase.Resources>
<src:ApplicationWindowBase.DataContext>
<Binding Mode="OneWay" Path="Filing" Source="{StaticResource ViewModelLocator}"/>
</src:ApplicationWindowBase.DataContext>
<Grid>
<!-- SNIP -->
<xxx_Windows_Core_Controls:TemplateEditor Grid.Column="1" Grid.Row="1" Margin="0" Text="{Binding Comment, Mode=TwoWay}" d:LayoutOverrides="Width, Height"/>
<!-- SNIP -->
</src:ApplicationWindowBase>
I am using MVVM Light and the ViewModel does bind correctly. There are other control/field bindings (ommitted) that work fine. THe problem is that the Text property binding on the Comment ViewModel property is not working. I can set it fine (ie. set value in ViewModel, then bind) but the user-entered value in Text never goes into the ViewModel Comments property.
What am I doing wrong?
Try this:
<TextBox Text="{Binding ElementName=userControl,Path=Text,Mode=TwoWay}" />
and remove handlers.
Ok, typical StackOverflow usage pattern adopted.
I realised I am writing this one-way, have added a TextChanged event handler to update the dependency property.
public partial class TemplateEditor : UserControl
{
public string Text
{
get
{
string s=(string)GetValue(TextProperty);
return s;
}
set
{
if (Text != value)
{
SetValue(TextProperty, value);
}
}
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(TemplateEditor),new PropertyMetadata(null,Text_PropertyChanged));
public TemplateEditor()
{
InitializeComponent();
textBox.TextChanged += new TextChangedEventHandler(textBox_TextChanged);
}
private static void Text_PropertyChanged(DependencyObject source, DependencyPropertyChangedEventArgs e)
{
((TemplateEditor)source).textBox.Text = (string)e.NewValue;
}
void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
Text = textBox.Text;
}
}
Thanks for your time, and apologies. :)
The content of the textblock is imported from a web service, but somehow there is a URL.
Is it possible to make it a link?
Thanks.
Sounds like you want a LinkLabel control. I've used that control with some modifications in my Silverlight Twitter Badge to mix the text and links that show up in tweets.
If you just have a TextBlock with a link only and want that clickable then you just set the cursor to be a hand and add an event handler for the MouseLeftButtonDown event that would navigate to the value of the TextBox.
Xaml:
<TextBlock Text="http://www.microsoft.com" Cursor="Hand" TextDecorations="Underline" MouseLeftButtonDown="TextBlock_MouseLeftButtonDown" />
Code:
private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var txt = ((TextBlock)sender).Text;
System.Windows.Browser.HtmlPage.Window.Navigate(new Uri(txt, UriKind.Absolute));
}
You could do something like the following; however this makes use of a Label and not a textblock.
In your XAML you do the following:
<dataInput:Label Grid.Row="2">
<ContentPresenter>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Hello world"/>
<HyperlinkButton x:Name="Test" NavigateUri="{Binding Path=URI}" Content="This is a url"/>
</StackPanel>
</ContentPresenter>
</dataInput:Label>
and in your code behind you add the following dependency property and set the datacontext to the page itself
public static readonly DependencyProperty URLProperty =
DependencyProperty.Register("URI", typeof(Uri), typeof(MainPage), null);
public Uri URI { get
{
return (Uri)GetValue(URLProperty);
}
set
{ SetValue(URLProperty, value); }
}
This code sets the dependency property for the binding to the URL;
public MainPage()
{
InitializeComponent();
URI = new Uri("/Home", UriKind.Relative);
DataContext = this;
}
This code creates a new URI and binds it to the variable. It also sets the data context to the page itself.
I have a custom UserControl with an image and a label, both of which are set at design-time in the XAML like so: <controls:HomeBarButton Icon="/SuCo;component/Resources/music.png" Text="music"/>
When the control has just an Icon, it looks fine. When I add the Text property, the icon disappears at both design- and run-time and the text label ignores the formatting set in the UserControl and is just black in the upper left corner of the control when the label is centered.
Relevant UserControl XAML:
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Image x:Name="icon" Width="102" Height="102" VerticalAlignment="Stretch" Source="{Binding Icon}"/>
<Label x:Name="label" HorizontalContentAlignment="Center" VerticalAlignment="Bottom" Foreground="White" FontFamily="Calibri" FontSize="24" Padding="0" Content="{Binding Text}"></Label>
</StackPanel>
Code-behind:
public ImageSource Icon
{
get { return (ImageSource)this.GetValue(IconProperty); }
set { this.SetValue(IconProperty, value); }
}
public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(ImageSource), typeof(HomeBarButton), new FrameworkPropertyMetadata(OnIconChanged));
private static void OnIconChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
dependencyObject.SetValue(Image.SourceProperty, e.NewValue);
}
public string Text
{
get { return (string)this.GetValue(TextProperty); }
set { this.SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(HomeBarButton), new FrameworkPropertyMetadata(OnTextChanged));
private static void OnTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
dependencyObject.SetValue(Label.ContentProperty, e.NewValue);
}
What am I doing wrong? :(
First, I would change that Label to a TextBlock--you would use Label to associate the text of the label with another control. It seems, from your code, that you are not doing this and only want to display the text. The other thing to check is if your text is being displayed over the top of your icon. I would guess that this is what is happening. Changing to a TextBlock may fix this, if not, you probably should set the height and with of the TextBlock manualy. Just my .02 worth.