AndyDB
AndyDB

Reputation: 431

Populating a text box using MVVM/LINQ and XAML

I am trying to learn about MVVM and LINQ, so am setting up a test database. The XAML based view has a single TextBox - bound to "OrgName". However, I've been working on an example, but I am having trouble with "var result" (I think).

For some reason, I get a runtime error under the heading "XamlParseException occurred", which I think is down to the query string, not pulling in the field.

As requested the full error is: {"'The invocation of the constructor on type 'WpfApplication2.MainWindow' that matches the specified binding constraints threw an exception.' Line number '4' and line position '9'."}

public partial class MainWindow : Window
{
    private egwEntities db = new egwEntities();
    MyViewModel mvm = new MyViewModel();

    public MainWindow()
    {
        InitializeComponent();

        this.DataContext = mvm;

        var result = (from o in db.egw_addressbook select o).FirstOrDefault();
        if (result != null)
        {
            mvm.OrgName = result.org_name;
        }
    }
}
    public class MyViewModel
    {
         public string OrgName { get; set; }
    }
}

Here is the XAMLI am using in the view:

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="EGW" Height="350" Width="525">
    <Grid>
        <TextBox Text="{Binding OrgName}" HorizontalAlignment="Left" Height="41" Margin="72,94,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="318"/>
    </Grid>
</Window>

Upvotes: 0

Views: 233

Answers (2)

AlanT
AlanT

Reputation: 3663

The error message indicates that the

var result = (from o in db.egw_addressbook select o).FirstOrDefault();
if (result != null) {
    mvm.OrgName = result.org_name;
}

is failing, causing the MainWindow construction to fail, giving the XamlParse exception. You can test this by removing these lines an seeing if it runs.

MVVM
One of the goals of the MVVM shape is to reduce linkage between the View (window) and the data. In the example above you are missing the Model from Model-View-ViewModel shape (or rather you have merged the Model and View).

You might try it this way.

Separate out the data access in to a repository - makes for easier testing

public interface IRepository {
    IEnumerable<Organization> GetOrganizations();
}

I have a used a dummy repository, you would replace this with your DataContext code

public IEnumerable<Organization> GetOrganizations() {
    return new[] {new Organization() {Name = "A name"}};
}

Add a model for the application

public class AppModel {

    public AppModel(IRepository repository) {

        var result = (from o in repository.GetOrganizations() select o).FirstOrDefault();

        if (result != null) {
            OrgName = result.Name;
        } 
    }

    public string OrgName { get; private set; }

}

Note:
This can be done in a few ways. We tend to have very thin ViewModels with a lot of member replication between the ViewModel and model (e.g. the OrgName appearing in both); the ViewModels being mainly pass-through and formatting. This is not the only way to do it (and may not even be the best). I would do a web search on MVVM shapes to follow up

I have tweaked the viewmodel code a bit. If you are not writing to the OrgName then it should be readonly

public class MainWindowViewModel {

    private readonly AppModel _model;

    public MainWindowViewModel(AppModel model) {
        _model = model;
    }

    public string OrgName {
        get { return _model.OrgName; }
    }

}

If the OrgName is readonly then the binding needs to be oneway. Adding a datacontext definition helps chasing down binding errors.

<Window x:Class="Sus.ProofsOfConcept.SimpleMvvm.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    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"
    xmlns:views="clr-namespace:MyExamples.SimpleMvvm"
    mc:Ignorable="d"
    d:DataContext="{d:DesignInstance views:MainWindowViewModel, IsDesignTimeCreatable=false }"
    Title="EGW" Height="350" Width="525">
<Grid>
    <TextBox Text="{Binding OrgName, Mode=OneWay}" HorizontalAlignment="Left" Height="41" Margin="72,94,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="318"/>
</Grid>

</Window>

The default running mechanism (startup Window named in App.Xaml) no longer works, absent messing around with Dependency Injection (DI) you can use

public partial class App : Application {

    protected override void OnStartup(StartupEventArgs e) {
        var model = new AppModel(new Repository());
        var viewModel = new MainWindowViewModel(model);
        var view = new MainWindow() {DataContext = viewModel};
        view.Show();
    }

}

Upvotes: 1

Mashton
Mashton

Reputation: 6415

The error is with your XAML, which will throw runtime errors if you have syntax errors. If there was a problem with your var result line you'd probably have received some other error.

Try changing your TextWrapping line to this:

TextWrapping=TextWrapping.Wrap

Upvotes: 0

Related Questions