Get instance of another element via Xaml Binding (Attached or DependencyProperty) - wpf

I have a use case where I want to get at a reference to an element in Xaml from another element.
For instance, consider this simplistic case. I have a UserControl called A and a UserControl called B,
and somehow I want to register an attached property where B can get the reference to A.
e.g.
<MyCustomControl Name="A"/>
<MyCustomControl Name="B"
AttachedPropClass.TheOtherControl="{Binding ElementName=A}"/>
So I would expect B.TheOtherControl to be equal to A. Is this possible in Xaml? Note I am not binding to a property of A, but rather I want the whole element.
Any solution using DependencyProperties, or AttachedProperties or Behaviors that lets me do this in Xaml would be great.
Edit: I'm attempting to do this in both WPF and Silverlight4. I have tried the above and it doesn't work, a property changed callback on the Attached property never gets hit.

You could use a Behaviour with a property that you set to the Control name and then search the logical Tree for the control. I have a similar thing where I want a certain event on one control to move focus to another control. I do this by specifying the control name to the Behaviour.
<TextBox Name="A"/>
<TextBox Name="B">
<Interactivity:Interaction.Behaviors>
<Behaviours1:ProgressNextOnEnterAction NextTextBoxControlName="A" />
</Interactivity:Interaction.Behaviors>
</TextBox>

Ok I think I figured out what happened.
Declare your dependency property like this:
private static readonly DependencyProperty TheOtherControlProperty =
DependencyProperty.RegisterAttached(
"TheOtherControl",
typeof(MyCustomControl),
typeof(AttachedPropClass),// Change this part
null);
public static MyCustomControlGetTheOtherControl(MyCustomControltarget)
{
return (MyCustomControl)target.GetValue(TheOtherControlProperty);
}
public static void SetTheOtherControl(MyCustomControltarget, TextBlock value)
{
target.SetValue(TheOtherControlProperty, value);
}
I think the issue is that you set the OwnerType of the Dependency Property to MyCustomControl instead of to AttachedPropClass.
I've created an example that works.
Give this a try and let me know if I'm right.
u_u

Related

WPF Binding to UserControl Custom DependencyProperty

I have a custom UserControl called SongDescription:
<UserControl x:Class="DPTestAp.SongDescription" ...>
<Grid x:Name="LayoutRoot">
<DockPanel Height="50">
<TextBlock x:Name="title" Text="{Binding name}" Width="100" Height="30"/>
<TextBox x:Name="lyrics"/>
</DockPanel>
</Grid>
</UserControl>
I added DependencyProperty to it:
public partial class SongDescription : UserControl
{
public static readonly DependencyProperty SongProperty = DependencyProperty.Register("Song", typeof(Song), typeof(SongDescription));
public Song Song
{
get
{
return (Song)GetValue(SongProperty);
}
set
{
SetValue(SongProperty, value);
updateLyrics()
}
}
private void updateLyrics()
{
lyrics.Text = Song.lyrics;
}
public SongDescription()
{
InitializeComponent();
}
}
The question is: how to bind something to this SongProperty?
I use SongDescription in my main window like this:
<local:SongDescription x:Name="songDescription" Song="{Binding DataContext}"/>
I cannot make my TextBox lyrics show lyrics. In main window I tried to set DataContext to songDescription, like this:
songDescription.DataContext = new Song() { name="Home", lyrics="Hold on, to me as we go" };
or to window itself like this:
DataContext = new Song() { name="Home", lyrics="Hold on, to me as we go" };
I even tried to make Song a resource and bind it to SongProperty like this:
<Window.Resources>
<local:Song x:Key="res" name="Home" lyrics="Hold on, to me as we go"/>
</Window.Resources>
<Grid>
<local:SongDescription x:Name="songDescription" Song="{StaticResource res}"/>
</Grid>
Nothing helped. TextBlock title binds song name fine. But I can't make updateLyrics() method be called. (In real life this method is more complicated, so I can't use Binding like with name).
Thank you!
Yup, so that's a gotcha with dependency properties. You never ever put validation code inside of the accessor methods (get/set) because dependency properties are stored by WPF in a table that it itself manages. This is why you have to register dependency properties, it essentially creates entries on this table for storing the values associated with each property, and when you use 'GetValue' / 'SetValue' you are updating the entries on this table (which by the way relates to how WPF is able to manage data bindings in general).
The upshot of this though is that WPF can (and will) completely bypass your property accessors because it has direct access to the real data. Why should it use your accessors if it can just go to the data directly. Instead you need to implement a 'PropertyChanged' callback function or some WPF sanctioned method of doing validation, but never ever do it in your accessors.
See:
http://msdn.microsoft.com/en-us/library/ms752914.aspx
In addition to sircodesalot's answer, you are not bound on your lyrics textbox. Also, since the song your bound to is a class, you will need to specify the paths fully for the properties you want to show in the boxes such as "Path=Song.Lyrics".
Another thing to consider is that with dependency properties; your mode will be oneway by default so making the text field editable would be moot really unless you change it.
Third, if you're using MVVM you only need your main window context to be set to the view model and have a matching Song property to bind against.

c# wpf Binding Command - access value of textbox from command

I want to call a simple Command which adds values from my GUI into the database.
My Command:
private ICommand addSpeechCommand;
public ICommand AddSpeechCommand
{
get
{
if (addSpeechCommand == null)
{
addSpeechCommand = new RelayCommand(param => AddSpeech());
}
return addSpeechCommand;
}
}
public void AddSpeech()
{
// TODO add
OrganizerBL.InsertSpeech(new Speech(maxSpeechId, currentSpeech.Title, currentSpeech.Summary, currentSpeech.Start, currentSpeech.End, currentSpeech.TrackId, currentSpeech.SpeakerId, currentSpeech.ConferenceId, currentSpeech.Room));
this.LoadSpeeches();
}
-- this commented out row shows how i dealt with it when a row of my datagrid was selected. but i want it to work without a currentSpeech
My XAML:
<Label x:Name ="lblTitle" Content="Title"/>
<TextBox x:Name="txtTitle" Text="{Binding CurrentSpeech.Title, Mode=TwoWay}" Margin="2,144,0,0" Height="20" VerticalAlignment="Top"/>
and other textboxes...
I really don't know how to access the values of the textboxes from the command to call my insertSpeech method...
Sorry for my english :)
UPDATE:
I'm getting a nullreference exception because my currentSpeech is null.
Is there a way to solve this without the currentSpeech?
The reason you get the NullReferenceException is probably because it's instanced in the property itself. When you cerate a binding, it's created to the property as it is at that stage. And you bind to the property when it's NULL. IT's actually created inside the property, but the Binding will never know that.
First of all, I would remove all logic from properties.
I would also implement the INotifyPropertyChanged to the class, and call the PropertyChanged in the property's "set". This means that the UI will know of any changes to the porperty.
Then I would create a depencency property for the property, if it's used in any Binding ot XAML.
Last, I would instance the command in the class's constructor.
Logic don't (in my book) belong to properties.
How to do
1. Bind TextBox.Text to View model property
2. Use View model property in Command Handler.
In your case, You have binded TextBox.Text to CurrentSpeech.Title, but using this.Title.
In you command, change this.Title to currentSpeech.Title

DataBinding Text Property of UserControl to ObservableCollection does not update

I have created a UserControl, which is to display a converted string value based on the contents of a bound ObservableCollection. Everything works when the application loads; my IValueConverter is called and produces the correct string result, which is displayed correctly in my UserControl. However if the ObservableCollection contents change, my control is not updated.
Also, before I created this control, I had the same behaviour, but binding the Content property of a regular Button control, and this also worked correctly and updated as expected.
Any ideas what I am missing to get the same thing with my UserControl?
The control property looks like;
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyUserControl));
public string Text
{
get { return GetValue(TextProperty) as string; }
set { SetValue(TextProperty, value);
}
The relevant section in the UserControl XAML (which displays the converted string value) is;
<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type Controls:MyUserControl}}, Path=Text}" />
And the control is created in a separate Window like so;
<CoreControls:MyUserControl
Name="myControl"
Text="{Binding Path=ObservableCollectionInstance, Converter={StaticResource MyValueConverter}, Mode=OneWay}" />
I would use ElementName instead of RelativeSource in your binding, since you have named your user control. Also, you are trying to bind a collection to a <Textbox>. a <Textbox> is designed to display a single item. this is probably why its not working. ObservableCollection fires CollectionChanged events, not PropertyChanged. Even if it did respond, you are still going to have problems because ObservableCollection does not notify when an item contained in it has property changes--only when items are added/removed etc (think, the collection itself changes). If this is the behavior you want, you are going to have to write some code.
EDIT
after your comments, it sounds to me like even though you set it to OneWay binding mode, its acting like OneTime binding mode.
I would try this to help you debug it:
add this xmlns:
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
and then, in your binding add this:
diagnostics:PresentationTraceSources.TraceLevel=High
here is an article on debugging bindings.
the other thing you could do is set breakpoints in your converter. see if its actually updating when you add/remove things to your collection. I would be willing to bet that its bc the ObservableCollection is NOT firing PropertyChanged events and that the initial update occurs because its not based on an update event.
ObservableCollection notifies only in case if items get added or removed. It is used to observe a collection. They are more suited for content controls. Read about it here. You are talking about observing a property, which needs INotifyPropertyChanged. Posting more code might help, like how are you changing the value of the collection.
Thanks for the tips guys.
I managed to work out a solution; I can handle the CollectionChanged event on the ObservableCollection and then explicitly update the target with something like;
BindingExpression exp = myControl.GetBindingExpression(MyUserControl.TextProperty);
if (null != exp) exp.UpdateTarget();
As noted, most likely, binding on the Text property is only listening to PropertyChanged events, not NotifyCollectionChanged events, but this solution does the trick.

Silverlight: How to add a Dependency Property to multiple controls?

Is it possible to create a Dependency Property for multiple controls without resorting to subclass every one of it?
I thought about using Attached Properties but they nest only one level deep as far as I understand it.
I want something like this to be possible:
<!-- MyDataGrid implements the new Attached Properties SourceData and TargetData -->
<MyDataGrid>
<StackPanel>
<TextBox MyDataGrid.SourceData="{Binding Somewhere}" MyDataGrid.TargetData="{Binding Somewhere}" />
</StackPanel>
<CheckBox MyDataGrid.SourceData="{Binding Somewhere}" MyDataGrid.TargetData="{Binding Somewhere}" />
</MyDataGrid>
This won't work since the Attached Properties wouldn't be found in the TextBox since it's no direct descendent of MyDataGrid.
Background is that I try to automatically convert an old Xaml-like Gui-syntax into real Xaml and with the old system it was possible to set different sources and targets for changed data. Now I'm searching for a Xaml-solution that doesn't involve subclassing every control there is.
Thanks in advance.
are you sure you are using Attached property correctly?
public static readonly DependencyProperty SourceDataProperty = DependencyProperty.RegisterAttached(
"SourceData", typeof (string), typeof (MyDataGrid), new PropertyMetadata("test"));
public static void SetSourceData(DependencyObject obj, string sourceData)
{
obj.SetValue(SourceDataProperty, sourceData);
}
public static string GetSourceData(DependencyObject obj)
{
return (string) obj.GetValue(SourceDataProperty);
}
This worked for me.Though SetSourceData was not get called, but data was there.
To retrive data.
MyDataGrid.GetSourceData(tbox);
Where tbox is the instance of your TextBox.

How to change TextBlock Text from codebehind?

I have a custom control. There is a Stack Panel with Button and TextBlock in generic.xaml:
<StackPanel>
<TextBlock x:Name="StatusText" />
</StackPanel>
Then I have
public class MyClass : Control
{
// Constructor etc.
public static readonly DependencyProperty StatusTextProperty = DependencyProperty.Register("StatusText", typeof(TextBlock), typeof(MyClass), null);
public TextBlock StatusText
{
get { return (TextBlock)this.GetValue(StatusTextProperty); }
set { SetValue(StatusTextProperty, value); }
}
}
There is if with some logic in that happens after the button is clicked.
How do I change the Text property of TextBloc?
I thought that I can do something like this
StatusText.SetValue(TextBlock.TextProperty, "Some text here.");
But it always returns NullReferenceException (Object reference not set to an instance of an object.)
Should I use PropertyChangedCallback() on dependency property or what else do I need? I am missing something ;-)
You're taking the wrong approach - instead of trying to push the text into the text block from the control's class, you need the text block to pull the value from the control's class. The main steps you need to do are:
Change the type of the dependency property from TextBlock to string.
Bind the Text property of the TextBlock in your control template to the dependency property using a TemplateBinding binding expression. Something along the lines of:
<TextBlock Text="{TemplateBinding StatusText}" />
You can then simply set the text to be displayed to the property on your control.
Hope this helps...
Chris
You can type your question on google and find answer few times faster.

Resources