10

How can I bind a control inside a usercontrol resource to a property? Alternatively, can I find the control from the code behind and get & set the value from there?

Here is my markup. I've stripped it down to just the relevant part:

Salesmen.xaml:

<UserControl.Resources>
            <ControlTemplate x:Key="EditAppointmentTemplate1" TargetType="local:SchedulerDialog" x:Name="ControlTemplate">
                <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
                    <Grid>
                        <Grid Name="grdTotal" Grid.Row="4" Visibility="{Binding ResourceTypesVisibility}">
                            <TextBox x:Name="totalSalesmen" Grid.Row="0" Grid.Column="1" Margin="3" Width="120" Text="{Binding Parent.totalSalesmen, ElementName=LayoutRoot, Mode=TwoWay}" />
                        </Grid>
                </ScrollViewer>
            </ControlTemplate>
    <Style x:Key="EditAppointmentDialogStyle1" TargetType="local:SchedulerDialog">
        <Setter Property="Template" Value="{StaticResource EditAppointmentTemplate1}" />
    </Style>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White" HorizontalAlignment="Left" Margin="10,10,0,0">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <StackPanel Orientation="Vertical">
        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" BorderBrush="Transparent">
            <telerik:RadCalendar Name="RadCalendar" SelectedDate="{Binding CurrentDate, ElementName=RadScheduleViewTests, Mode=TwoWay}" IsTodayHighlighted="True"
                         telerik:StyleManager.Theme="Metro" HorizontalAlignment="Left" VerticalAlignment="Top" FirstDayOfWeek="Sunday" Margin="0,0,15,0"
                         SelectionChanged="RadCalendar_SelectionChanged_1"  >
            </telerik:RadCalendar>
        </ScrollViewer>
    </StackPanel>

    <telerik:RadScheduleView Name="RadScheduleViewTests"  MinAppointmentWidth="100" Tag="{Binding Path=Context, ElementName=TestDayPage}"
                             telerik:StyleManager.Theme="Metro" Grid.Column="1" EditAppointmentDialogStyle="{StaticResource EditAppointmentDialogStyle1}"
                             AppointmentCreating="RadScheduleViewTests_AppointmentCreating_1" AppointmentEditing="RadScheduleViewTests_AppointmentEditing_1"
                             AppointmentDeleting="RadScheduleViewTests_AppointmentDeleting_1" FirstDayOfWeek="Sunday" ShowDialog="RadScheduleViewTests_ShowDialog_1"
                             AppointmentEdited="RadScheduleViewTests_AppointmentEdited_1">
        <telerik:RadScheduleView.DragDropBehavior>
            <examiners:CustomDragDropBehaviour/>
        </telerik:RadScheduleView.DragDropBehavior>
        <telerik:RadScheduleView.SchedulerDialogHostFactory>
            <test:CustomScheduleViewDialogHostFactory />
        </telerik:RadScheduleView.SchedulerDialogHostFactory>
        <telerik:RadScheduleView.ViewDefinitions>
            <telerik:DayViewDefinition/>
            <telerik:WeekViewDefinition/>
            <telerik:MonthViewDefinition/>
            <telerik:TimelineViewDefinition/>
        </telerik:RadScheduleView.ViewDefinitions>
    </telerik:RadScheduleView>
</Grid>

And here's my property. Despite the two-way binding it is always null:

Salesmen.xaml.cs:

string totalSalesmen { get; set; }

I've heard about the VisualTreeHelper and the LogicalTreeHelper. These might enable another approach - finding the control and getting and them manually. However, VisualTreeHelper only sees the LayoutRoot and it's children (not UserControl.Resources), and LogicalTreeHelper does not seem to be available (it's a SilverLight 5 project; I don't know what framework is used by Silverlight 5. I understand that LogicalTreeHelper is only available in 4.5 and later)

Thank you for you assistance. Note: this question will get a +50 bounty. The system requires me to wait for 2 days to put a bounty, so I will put the bounty and accept the answer after 2 days. I will let you know if your answer works before that.

Zesty
  • 2,922
  • 9
  • 38
  • 69
  • Hi, the bounty will not be awarded if you do not assign it manually. – Martin May 21 '15 at 12:57
  • :((( Has the system changed? I thought it would get awarded automatically when the answer was selected. I got a message from the system saying 'Your bounty on question "How can I bind or otherwise get & set a value of a control in a resource?" is completed and will be auto-awarded in 24 hours.'. Please confirm if you have received it. – Zesty May 24 '15 at 12:14
  • No, haven't received it. But I think you can still award bounty points to an answer you like, independent from the question-bounty system. – Martin Jun 01 '15 at 07:51
  • Ah, after reading the documentation, I think it awarded you only half the bounty, as it was automatically awarded. I don't like these "gotchas" with the bounty system - it really should be more straightforward. I was under the impression that selecting a correct answer would itself award the bounty (what possible reason could I have to mark one answer as the correct one and yet give a different one the bounty? – Zesty Jun 08 '15 at 07:29
  • Anyway, I will award a bounty on another of your answers. I will award it on http://stackoverflow.com/questions/26673509/cant-i-use-apostrophe-in-stringformat-of-xaml-binding-silverlight/26675176#26675176. I now know that I have to award it manually :) – Zesty Jun 08 '15 at 07:32
  • It did not award half the bounty, it did award nothing. Auto-Bounty can only be awarded for answers that were created **after** the bounty was set. – Martin Jun 08 '15 at 08:04
  • Thanks for going through all the hassle to still assign a bounty, I sure owe you a beer. – Martin Jun 08 '15 at 08:10

2 Answers2

13

Your Binding to totalSalesmen and everything inside the EditAppointmentTemplate1 will not have any effect as long as the Template is never instantiated. Think of a Template (both ControlTemplate and DataTemplate) as a blueprint. The elements that are defined inside are only instantiated when the template is used somewhere.

Do you have a usage somewhere? like this:

<Grid>
    ...
    <SchedulerDialog Template="{StaticResource EditAppointmentTemplate1}"/>
    ...
</Grid>

[Edit #1]

Ok let's see... your twoway Binding to totalSalesmen looks ok, albeit a bit smelly. I think the property totalSalesmen should rather live in the DataContext (and it would be easier to bind against). But first let's try to make your code work, maybe we make it nice later:

The Problem

When (in one single xaml file) using ElementName in Bindings while at the same time using templates to define parts of the UI (and remember: the stuff in templates is only created when it is used somewhere, and the creation may happen at a different point in time) there is the risk, that elements that you expect to know each other are in fact created in diffent NameScopes. And your affected ElementName-Bindings won't work. Just silently won't work.

A Cure

You can try a trick: Ensure that you have a StaticResource that holds the reference to the element you originally wanted to use by ElementName. And then you just write a Binding against that StaticResource. See what I've done here:

<UserControl x:Class="Salesmen" ... x:Name="me">
  <UserControl.Resources>
    <BindableObjectReference x:Key="MyUserControl" Object="{Binding ElementName=me}"/>
    <ControlTemplate x:Key="EditAppointmentTemplate1"
        TargetType="local:SchedulerDialog">
        ...
        <TextBox Text="{Binding Path=Object.totalSalesmen,
            Source={StaticResource MyUserControl}, Mode=TwoWay}"/>
        ...
    </ControlTemplate>
  </UserControl.Resources>

and the code

public class BindableObjectReference : DependencyObject
{
    public object Object
    {
        get { return GetValue( ObjectProperty ); }
        set { SetValue( ObjectProperty, value ); }
    }

    public static readonly DependencyProperty ObjectProperty =
        DependencyProperty.Register( "Object", typeof( object ),
        typeof( BindableObjectReference ), new PropertyMetadata( null ) );
}

[Edit #2]

When you bind to a property of the DataContext, you just specify the path but not the source (implicitly the source will be the DataContext):

Text="{Binding Path=totalSalesmen, Mode=TwoWay}"
Martin
  • 5,714
  • 2
  • 21
  • 41
  • +1. Sorry, I missed the schedulerDialog while posting, as I was trying to keep the code to a minimum. It still comes null. Is the binding expression {Binding Parent.totalSalesmen, ElementName=LayoutRoot, Mode=TwoWay} correct? – Zesty May 11 '15 at 09:46
  • 1
    Best thing would be to edit your question and show the relevant part. I have an idea what's wrong, but when I can't see the relevant parts its just speculation. – Martin May 11 '15 at 10:04
  • 1
    @Zesty: ? I can't see where and how you are using the template. – Martin May 11 '15 at 10:13
  • I've added the Is this what's required? – Zesty May 11 '15 at 10:16
  • Thanks for the very detailed answer, Martin. I'm not able to add , even though I've defined the class within the same namespace of the XAML page. Does it need to implement any interface, or do I have to add any XML namespace at the top of the page to get it to work? – Zesty May 11 '15 at 13:06
  • On the other hand, I was able to add totalSalesmen as a property within the class that is the DataContext for the template (thanks to your post!). I've also verified that it is getting and setting properly to the EF. Unfortunately, I still can't get the binding to work. Is my syntax correct (assuming that I've added it to the DataContext): – Zesty May 11 '15 at 13:08
  • 1
    You have to add a xaml namespace when you use your own classes: I use resharper and therefore not really think about the xaml namespaces because resharper will suggest the correct namespace and it's only one click for me with the mouse. – Martin May 11 '15 at 14:54
  • 1
    And regarding your second question: the Binding is wrong, when you bind to properties in the DataContext you do not use the ElementName but just set the path: Text="{Binding Path=totalSalesmen, Mode=TwoWay}" – Martin May 11 '15 at 14:59
1

If you Add TotalSalesmen to the DataContext of the template, e.g.

public class SpecialAppointment: Appointment
{
  private int _totalSalesmen = 0;
  public int TotalSalesmen 
  {get {return _totalSalesmen; }
  {set {_totalSalesmen = value; OnPropertyChanged(()=> this.TotalSalesmen);}
}

You should be able to bind the text box thus:

<TextBox Text="{Binding TotalSalesmen, Mode=TwoWay}" />

Note the binding is case-sensitive, and it must match the property name TotalSalesmen and not the field _totalSalesmen.

Chui Tey
  • 5,436
  • 2
  • 35
  • 44