ChandlerPelhams
ChandlerPelhams

Reputation: 1678

Execute Method in User Control MVVM

I am working on my first MVVM application and am having trouble getting it to work properly.

In my main window, I have a button that executes a SQL command that returns a custom data table type object.

The window also contains a user control that consists of a some column headers and a windows forms hosted DataGridView. I need to somehow tell the user control to execute a method that passes the data to the DataGridView so it can update it's values.

I tried creating a dependency property on my WPF Grid control that is bound to the data of my viewmodel, but it is not updating properly.

How can I get this to work?

--EDIT--

Here is the XAML for my LiteGrid usercontrol -

<UserControl x:Class="ReportUtility.Controls.LiteGrid.LiteGrid"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
         xmlns:lite="clr-namespace:ReportUtility.Controls.LiteGrid"
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300"
         DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="30"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <ScrollViewer x:Name="_scroll" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Hidden">
        <ItemsControl ItemsSource="{Binding Columns}" Grid.Row="0" Background="AliceBlue">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate> 
                        <StackPanel Orientation="Horizontal"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </ScrollViewer>
    <WindowsFormsHost Background="White" Grid.Row="1">
        <lite:LiteGridView x:Name="_liteGridView"/>
    </WindowsFormsHost>
</Grid>

My main view model is:

public class MainWindowViewModel : DependencyObject
{
    private readonly ILiteTableSource _source;

    public ICommand ExecuteQueryCommand { get; set; }
    public LiteGridViewModel Grid { get; set; }
    public string SqlCommandText { get; set; }

    public MainWindowViewModel(ILiteTableSource source)
    {
        this.ExecuteQueryCommand = new ExecuteQueryCommand(this);

        _source = source;
        _source.DataArrived+=new Action<DataSources.LiteSource.LiteTable>(_source_DataArrived);
    }

    public void ExecuteQuery()
    {
        _source.Connection = new ServerConnection();
        _source.CommandText = this.SqlCommandText;
        _source.ExecuteQuery();
    }

    public LiteTable Results
    {
        get { return (LiteTable)GetValue(ResultsProperty); }
        set { SetValue(ResultsProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Results.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ResultsProperty =
        DependencyProperty.Register("Results", typeof(LiteTable), typeof(MainWindowViewModel), new UIPropertyMetadata(null));

    void _source_DataArrived(LiteTable data)
    {

        this.Results = data;
    }
}

And the XAML:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="auto"/>
        <RowDefinition Height="50"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Button Grid.Row="0" Content="TestButton" HorizontalAlignment="Stretch" Command="{Binding ExecuteQueryCommand}"/>
    <TextBox Grid.Row="1" Text="{Binding Path=SqlCommandText, UpdateSourceTrigger=PropertyChanged}"/>
    <lite:LiteGrid Grid.Row="2" Data="{Binding Data, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>

Upvotes: 4

Views: 2241

Answers (2)

Saad Imran.
Saad Imran.

Reputation: 4530

public LiteTable Data
{
    get { return (LiteTable)GetValue(DataProperty); }
    set 
    {
        object oldvalue = Data;
        SetValue(DataProperty, value);
        OnPropertyChanged(new DependencyPropertyChangedEventArgs(DataProperty, oldvalue, value));
    }
}

// Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
    DependencyProperty.Register("Data", typeof(LiteTable), typeof(LiteGrid), new UIPropertyMetadata(null));

try this...

Upvotes: 0

Rachel
Rachel

Reputation: 132618

Since you have a LiteGridViewModel to go with your LiteGrid, why not just execute the command from the ViewModel?

Using the code posted in your question, I would:

Add this to the Resources to make sure that LiteGridViewModel is drawn using LiteView

<Window.Resources> <!-- Or Grid.Resources if you prefer -->
    <DataTemplate DataType="{x:Type lite:LiteGridViewModel}">
        <lite:LiteGrid />
    </DataTemplate>
</Window.Resources>

Replace the <lite:LiteGrid ... /> control in the MainView with a ContentControl to display the ViewModel

<ContentControl Content="{Binding Grid}" />

Remove the Data Property on the MainViewModel since it should be stored in LiteGridViewModel, not MainViewModel

And in your MainWindowViewModel work with the LiteGridViewModel instead of trying to work with the ViewModel via the View

Grid = new LiteGridViewModel();

void _source_DataArrived(LiteTable data)
{
    Grid.Data = data;  // Fill property in ViewModel
    Grid.UpdateData(); // Call command on ViewModel
}

Upvotes: 4

Related Questions