views:

214

answers:

5

I am having trouble databinding a TextBox.Text property to a object's method. The ideia is allowing the user to write in a textbox a file name and then have a TextBlock output that file's extension.

class GetFileInfo
{
    public string GetFileExtension(string fileName)
    {
        return Path.GetExtension(fileName);
    }
}

Here is my XAML:

<Window.Resources>
    <ObjectDataProvider x:Key="getFileInfo" MethodName="GetFileExtension" ObjectType="{x:Type local:GetFileInfo}">
        <ObjectDataProvider.MethodParameters>
            <sys:String>abc.text</sys:String>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

<StackPanel>
    <TextBox Name="textBox1">
        <TextBox.Text>
            <Binding Source="{StaticResource getFileInfo}" Path="MethodParameters[0]" BindsDirectlyToSource="True" UpdateSourceTrigger="PropertyChanged" />
        </TextBox.Text>
    </TextBox>
    <TextBlock Name="textBlock1" Text="{Binding Source={StaticResource getFileInfo}}"/>
</StackPanel>

For some reason it is not doing anything. Anyknow could point out what may be the reasons? Here is what I see on the designer and when I run the application:

alt text

And here is what happens when I try setting other text at run-time:

alt text Here is the error given by de debugger when trying to set other text at run-time:

System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=MethodParameters[0]; DataItem='ObjectDataProvider' (HashCode=2207369); target element is 'TextBox' (Name='textBox1'); target property is 'Text' (type 'String') ArgumentException:'System.ArgumentException: Object of type 'MS.Internal.Data.PropertyPathWorker+IListIndexerArg' cannot be converted to type 'System.Int32'. at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) at System.RuntimeType.CheckValue(Object value, Binder binder, CultureInfo culture, BindingFlags invokeAttr) at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture) at MS.Internal.Data.PropertyPathWorker.SetValue(Object item, Object value) at MS.Internal.Data.ClrBindingWorker.UpdateValue(Object value) at System.Windows.Data.BindingExpression.UpdateSource(Object value)'

A: 

Is the DataContext set? In your code-behind, did you set the value for your TextBlock to "saadsas" (I can only guess), which broke your databinding?

Dave
I edited my original post. Basically everything I wrote in my code files is there. I didn't set a DataContext. In fact, im googling right now what a DataContext is.
devoured elysium
A: 

Databinding requires the NotifyPropertyChanged event to be called when the source is updated. In this case, you'd want to wrap this function call in a get/set like so:



public class FileWrapper: System.ComponentModel.INotifyPropertyChanged{
    private string m_filename;

    public string FileExtension{
        get{ return GetFileExtension(FileName);}
    }

    public string FileName{
        get{ return m_filename;}
        set{ m_filename = value; OnPropertyChanged("FileName"); OnPropertyChanged( "FileExtension");
    }

    public string GetFileExtension( string filename){
        //implementation
    }

    public event System.ComponentModel.NotifyPropertyChangedEvent PropertyChanged = (a,b)=>{};

    protected void OnPropertyChanged(string property){
        PropertyChanged( this, new System.ComponentModel.PropertyChangedEventArgs( property ));
    }
}

apandit
I'm going to try this code. But isn't there a simpler solution to just run some method from a given class given some parameters?
devoured elysium
@devoured, If you prefer to do in in proper WPF way, this should be the code you need to use. There is nothing crazy going on in the above code, if you learn this it is gonna help you forever with WPF. in any MVVM based WPF you can see this small piece of code snippet. So it worth a learning.
Jobi Joy
I see your point. I'll learn it this way then.
devoured elysium
But where should this be put? Should FileExtension be a Window's property?
devoured elysium
+1  A: 

Looks like you need to get an understanding of MVVM , check this classic article http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

Jobi Joy
Yes, but as pointed in my post, the idea is to type what you want in the textbox at run-time. I know that it is doing what he should with the string I'm giving him at design time inside the <string> tags.
devoured elysium
A: 

Ok, seems like this a bug when running WPF 4.0, as can be seen in comments here.

Woops, I was a bit hasty .. the example works perfectly as long you compile it for the 3.5 framework (in VS 2010). But if you convert it to WPF 4.0 project the method WeightOnPlanet on ObjectDataProvider is nolonger invoked on odp2 when you edit the textbox. I have tried to find any new Attributes on either the Binding or the ObjectDataProvider – but nothing has worked out so far …

When compiling in 3.5, it works fine here.

devoured elysium
+1  A: 

While it's possible to use Binding to call a method and get its return value, it's not straightforward. It's much simpler to bind to properties, and to use the combination of binding and change notification to get the result you're looking for.

Create a class with two properties, Filename and Extension. Filename is just a read/write string property. Extension is a read-only string property whose getter calls the method that you're trying to call.

Now make that class implement INotifyPropertyChanged, because if a property can change in code, it needs a way of telling the binding that it has done so. Make the setter of the Filename property notify bindings that the Extension property has changed.

Add a Binding to a TextBox that binds to the Filename property using the TwoWay mode. Add a Binding to a TextBox that binds to Extension using the default OneWay mode.

The sequence of events is:

  1. User types a new Filename into a bound TextBox and presses TAB.
  2. The TextBox loses focus.
  3. Because the Binding's mode is TwoWay, and it's using the default behavior of updating the source when the target loses focus, that's what it does.
  4. The Binding updates the source by calling the Filename setter.
  5. The Filename setter raises PropertyChanged.
  6. The Binding handles PropertyChanged, looks at its argument, and sees that the Extension property has changed.
  7. The Binding calls the Extension property's getter.
  8. The Extension property's getter calls the method to determine the extension for Filename, and returns it to the Binding.
  9. The Binding updates its target TextBox with the new value of Extension.

This is the core concept underlying data binding and the MVVM pattern. Once you understand it, it becomes second nature, and WPF development becomes about ten million times easier.

Robert Rossney