2
votes

I have a little problem with MVVM. Let me first sketch my problem.

I have a Parent View (DashboardConsultants) which has a datagrid. Each cell in that DataGrid has a tooltip, implemented like this:

 <UserControl.Resources>
        <ResourceDictionary>
            <DataTemplate DataType="{x:Type vm:UC1001_AgreementDetailsViewModel}">
                <v:UC1001_AgreementDetails_View />
            </DataTemplate>    
        </ResourceDictionary>
 </UserControl.Resources>

<DataGridTextColumn.ElementStyle>
      <Style TargetType="{x:Type TextBlock}">
      <Setter Property="DataGridCell.ToolTip">
              <Setter.Value>
                   <vm:UC1001_AgreementDetailsViewModel />
              </Setter.Value>
      </Setter>

The tooltip calls up my ViewModel (AgreementDetailsViewModel), which has the following code:

public UC1001_ActiveAgreementContract AgreementDetailsContract { get; set; }

public int AgreementID { get; set; }

public UC1001_AgreementDetailsViewModel()
{
    AgreementDetailsContract = new UC1001_ActiveAgreementContract();
    this.Initialize();
}

private void Initialize()
{
    GetRefData();
    ShowAgreementDetailsView();
}

private void GetRefData()
{
    UC1001_ActiveAgreementArguments args = new UC1001_ActiveAgreementArguments();
    args.AgreementID = 3;
    DefaultCacheProvider defaultCacheProvider = new DefaultCacheProvider();
    if (!defaultCacheProvider.IsSet("AgrDet:" + args.AgreementID))
    {
        ConsultantServiceClient client = new ConsultantServiceClient();

        AgreementDetailsContract = client.GetAgreementDetailsByAgreementID(args);
        defaultCacheProvider.Set("AgrDet:" + args.AgreementID, AgreementDetailsContract, 5);
    }
    else
    {
        AgreementDetailsContract = (UC1001_ActiveAgreementContract)defaultCacheProvider.Get("AgrDet:" + args.AgreementID);
    }
}


private void ShowAgreementDetailsView()
{
    // Initialize
    var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();

    // Show content
    var agreementDetailsWorkspace = new Uri("UC1001_AgreementDetails_View", UriKind.Relative);
    regionManager.RequestNavigate("ContentRegion", agreementDetailsWorkspace);
}

The goal of the ViewModel is to get an Agreement from the database (currently a static one...) and then pass it on to the child View (UC1001_AgreementDetails_View) to show as the tooltip. The child View has the following constructor so the controls can bind to the contract:

public UC1001_AgreementDetails_View(ViewModels.UC1001_AgreementDetailsViewModel UC1001_AgreementDetailsViewModel)
    {            
        InitializeComponent();
        this.DataContext = UC1001_AgreementDetailsViewModel.AgreementDetailsContract;
    }

I put a breakpoint on the ViewModel Initialize, and it fires when I get on the parent View, but it should fire when I get on the child View (thus when opening the tooltip in the datagrid). Does anyone know how I can fix this?

More information/code can be provided if needed.

EDIT:

I tried some stuff and I now I got something like this (which I feel is a little closer to the solution).

I changed my tooltip to the following (according to Rachels help):

<Setter Property="DataGridCell.ToolTip">
          <Setter.Value>
                <v:UC1001_AgreementDetails_View DataContext="{Binding AgreementDetailsViewModel}" />
          </Setter.Value>
</Setter>

In my child view, I put the following binding

<Label Content="{Binding AgreementDetailsContract.Header}" Height="50" HorizontalAlignment="Left" Margin="8,6,0,0" Name="_labelHoofding" VerticalAlignment="Top" FontSize="22" />

Now my AgreementDetailsViewModel, which has a property called AgreementDetailsContract with all the information I want to show, is the DataContext of my child view. But my problem still exist. The AgreementDetailsViewModels fires on opening the ConsultantDashboard, when it should open on displaying the tooltip. Is there some kind of event/command I can put on the tooltip to fire the ViewModel? Also, I think something is wrong with the Binding of my label because it doesn't show information (altough it could be the same problem that the ViewModel doesn't pass the right information).

Again, if it looks a little complex to you, I will be happy to explain it further, or give more code if asked.

SOLVED:

I got the solution. I specify the binding in the constructor of the ChildView instead of its XAML or in the View Tooltip.

public UC1001_AgreementDetails_View()
   {
      InitializeComponent();
      this.DataContext = new UC1001_AgreementDetailsViewModel();
   } 
1
Please, invest in a copy of the framework design guidelines. Either to read yourself, or to beat the person who decided the form of your type names. Also, on a more serious note, have you tried making a very simple prototype which repro's your issue? You seem to be doing lots of complex stuff which may break in several different ways... - user1228
The general idea of the functionality is not so hard. I just want to show a view as a tooltip with data coming from a ViewModel. This is the first time I use WPF and MVVM so I don't really have an idea how to fix this myself :) - Jelle Capenberghs
Tooltips exist outside of the logical tree, so they don't inherit the DataContext as expected. I can't spend enough time looking through your question to figure out if that is the root of your problem. You can search for databinding tooltips here and find lots of questions about the issues this involves. I think I've posted a few answers on the subject as well... - user1228
@Will I don't think that's correct... Menus are outside of the logical tree, but ToolTips should be in the same tree. I recently updated a project to move some details to a ToolTip and the bindings didn't change at all. - Rachel
Try binding the DataContext={Binding AgreementDetailsViewModel} inside your UserControl instead of binding it on the ToolTip - Rachel

1 Answers

3
votes

It looks like your View is directly referencing the ViewModel, which means it will create a copy of your ViewModel when it starts up

This code

<Setter Property="DataGridCell.ToolTip">
    <Setter.Value>
        <vm:UC1001_AgreementDetailsViewModel />
    </Setter.Value>
</Setter>

Should be

<Setter Property="DataGridCell.ToolTip">
    <Setter.Value>
        <!-- If you want to keep the DataTemplate, use a ContentControl -->
        <v:UC1001_AgreementDetails_View DataContext="{Binding AgreementDetails}" />
    </Setter.Value>
</Setter>

Your Data Structure should look something like this:

class MainViewModel
{
    ObservableCollection<AgreementViewModel> Agreements;
}

class AgreementViewModel
{ 
    // Loaded only when getter is called
    AgreementDetailViewModel AgreementDetails;
}