Ralf de Kleine
Ralf de Kleine

Reputation: 11734

Bind TextBox.TextProperty to a property of type Binding from the Model

I've got a list of command buttons (with input) I want to bind with the model. The thing is I want the textbox in the button to bind to somewhere (see viewmodel).

The following code is what I tried and failed. Is it (even) possible to set binding on the model then bind this to a control?

Or in other words am I trying to do something the stupid way?

View:

<ToolBar Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" ItemsSource="{Binding SelectedTab.Commands}" Height="34">
    <ToolBar.Resources>
        <DataTemplate DataType="{x:Type model:ZoekCommandButtons}">
            <Button Command="{Binding Command}" ToolTip="{Binding Tooltip}" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}">
                <StackPanel Orientation="Horizontal">
                    <Image Source="{Binding Image, Converter={StaticResource ImageConv}}" Height="16" Width="16"></Image>
                    **<TextBox Width="100" Text="{Binding Text}">**
                        <TextBox.InputBindings>
                            <KeyBinding Gesture="Enter" Command="{Binding Command}"></KeyBinding>
                        </TextBox.InputBindings>
                    </TextBox>
                </StackPanel>
            </Button>
        </DataTemplate>
    </ToolBar.Resources>
</ToolBar>

Model:

    public class ZoekCommandButtons : BaseModel, ICommandItem
    {
        private string _header;
        private string _image;
        private bool _isEnabled;
        private Visibility _isVisible;
        private ICommand _command;
        private string _tooltip;
        private Binding _text;


        public Binding Text
        {
            get { return _text; }
            set { _text = value; OnPropertyChanged("Text"); }
        }
(etc)

Viewmodel:

    Commands.Add(new ZoekCommandButtons()
    {
        Image = "search.png",
        IsEnabled = true,
        **Text = new Binding { RelativeSource = new RelativeSource(RelativeSourceMode.FindAncestor, typeof(UserControl), 1), Path = new PropertyPath("FilterText") },**
        Command = FilterCommand,
        Tooltip = "Zoeken",
        Header = "Zoeken"
    });

Upvotes: 0

Views: 192

Answers (2)

JerKimball
JerKimball

Reputation: 16894

First off, I would not recommend exposing Binding as a ViewModel property; in this particular case, it sounds more to me like you have nested ViewModels, and that approach would be far more suitable - that is, you have a "MamaViewModel" that has your "Commands" property, which is in turn a collection of "CommandButtonViewModels"...

Ok, That said...you can do this, although I must reiterate that you probably should not; what you're missing is "something to evaluate the Binding on" to provide a value. Here's a class that gives you that:

public static class BindingEvaluator
{
    // need a DP to set the binding to
    private static readonly DependencyProperty PlaceholderProperty = 
        DependencyProperty.RegisterAttached("Placeholder", typeof(object), typeof(DependencyObject), new UIPropertyMetadata(null));

    // Evaluate a binding by attaching it to a dummy object/property and evaluating the property value
    public static object Evaluate(Binding binding)
    {
        var throwaway = new DependencyObject();
        BindingOperations.SetBinding(throwaway, PlaceholderProperty, binding);
        var retVal = throwaway.GetValue(PlaceholderProperty);
        return retVal;
    }
}

That, combined with a ViewModel definition something like:

public class DontDoThisViewModel
{
    public Binding TextBinding {get; set;}
    public string Text 
    {
        get 
        {
            return BindingEvaluator.Evaluate(TextBinding) as string;
        }
    }
}

Should work...here's a test app I threw together in LINQPad:

void Main()
{
    var wnd = new Window() { Title = "My window" };
    var text = new TextBlock();
    text.Text = "Hopefully this shows the window title...";
    text.SetBinding(TextBlock.TextProperty, new Binding("Text"));
    wnd.Content = text;
    var vm = new ViewModel();
    var vmBinding = new Binding("Title");
    vmBinding.Source = wnd;
    vm.TextBinding = vmBinding;
    wnd.DataContext = vm;
    wnd.Show();
}

AGAIN, I must strongly recommend you NOT do this...but I was curious, so I had to come up with a way. ;)

Upvotes: 3

Ralf de Kleine
Ralf de Kleine

Reputation: 11734

Ok. I wasn't thinking straight.

Changed the Text property in the Model to string and handled the command with this property.

(although it would be nice to set binding on the model somehow...)

Upvotes: 0

Related Questions