2013-04-19 2 views
2

У меня возникла проблема, когда datagrid не отражает изменения в своей коллекции при прикреплении к представлению внутри представления. Точнее, у меня есть SecondView в MainView. На SecondView у меня есть datagrid с автогенераторами, установленными в true; когда сначала отображается рендеринг данных, он отображает соответствующие столбцы и заголовки. Однако, когда я заполняю список, который прикреплен к нему, изменения не отражаются.Просмотр в представлении, не обновляющемся - Caliburn.Micro

Вот полный код для двух мнений и их соответствующих ViewModels: MainWindowView:

<Window x:Class="MyApp.MainWindowView" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:cal="http://www.caliburnproject.org" 
    xmlns:views="clr-namespace:MyApp" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindowView" Height="300" Width="300"> 
<Grid> 
    <DockPanel> 
     <Menu DockPanel.Dock="Top"> 
      <MenuItem Header="File"> 
       <MenuItem Header="Open" x:Name="Open"/> 
       <MenuItem Header="Exit" x:Name="Exit"/> 
      </MenuItem> 
     </Menu> 
     <StackPanel DockPanel.Dock="Bottom"> 
      <views:SecondView/> 
     </StackPanel> 
    </DockPanel> 
</Grid> 

MainWindowViewModel:

namespace MyApp 
{ 
[Export(typeof(IShell))] 
internal class MainWindowViewModel : Screen, IShell 
{ 
    Regex expression = new Regex(@"^N\d\.C\d\.D\d\.R\d:\s\s\s-\d"); //ex. "N1.C1.D2.R1: -3"   
    SecondViewModel svm = new SecondViewModel();   
    public void Open() 
    { 
     Microsoft.Win32.OpenFileDialog openFile = new Microsoft.Win32.OpenFileDialog(); 
     openFile.Multiselect = true; 
     openFile.Filter = "Text Files(*.txt)|*.txt|Log Files(*.log)|*.log|All Files(*.*)|*.*"; 
     openFile.Title = "Open File(s)"; 
     bool? userClickedOK = openFile.ShowDialog(); 
     string[] _fileNames = openFile.FileNames; 
     if (userClickedOK == true) 
     { 
      if (_fileNames != null) 
      { 
       for (int i = 0; i < _fileNames.Length; i++) 
       { 
        ValidFiles(_fileNames[i]); 
       } 
      } 
     } 
    } 
    public void Exit() 
    { 
     App.Current.Shutdown(); 
    } 
    /* ValidFiles() accepts a string containing a filename and creates a Streamreader that reads the file if it is not a Boxboro file. 
    */ 
    public void ValidFiles(string filename) 
    { 
     string line; 
     using (StreamReader sr = new StreamReader(filename)) 
     { 
      while ((line = sr.ReadLine()) != null) 
      { 
       if (line.Contains("Mono Status")) 
       { 
        Console.WriteLine("File(s) not supported by this parser. Please select a valid file."); 
        break; 
       } 
       else 
       { 
        IsMatch(line); 
       } 
      } 
     } 
    } 
    /* IsMatch() accepts a string "input" and determines which parsing method to send the string to, if any. 
    * Strings not matching any of the initial criteria are not processed to limit overhead. 
    */ 
    public void IsMatch(string input) 
    { 
     Match match = expression.Match(input); 
     if (match.Success) 
     { 
      svm.GetData(input); 
     } 
    } 
} 

}

SecondWindowView:

<UserControl x:Class="MyApp.SecondView" 
     xmlns:cal="http://www.caliburnproject.org" 
     cal:Bind.Model="MyApp.SecondViewModel" 
     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" 
     mc:Ignorable="d" 
     d:DesignHeight="300" d:DesignWidth="300"> 
<Grid> 
    <StackPanel> 
     <DataGrid x:Name="MyList"/> 
    </StackPanel> 
</Grid> 

SecondWindowViewModel:

namespace MyApp 
{ 
[Export(typeof(SecondViewModel))] 
class SecondViewModel:Screen 
{ 
    Parse parse = new Parse(); 
    BindableCollection<MyObject> myList = new BindableCollection<MyObject>(); 
    MyObject myObject; 
    public MyObject MyObject 
    { 
     get { return myObject; } 
     set 
     { 
      myObject = value; 
      NotifyOfPropertyChange(() => MyList); 
     } 
    } 
    public BindableCollection<MyObject> MyList 
    { 
     get { return myList; } 
     set 
     { 
      MyList = value; 
      NotifyOfPropertyChange(() => MyList); 
     } 
    } 
    public void GetData(string input) 
    { 
     string[] tempArray = input.Split(); 
     List<int> tempList = new List<int>(); 
     for (int i = 1; i < tempArray.Length; i++) 
     { 
      if (!string.IsNullOrEmpty(tempArray[i])) 
      { 
       tempList.Add(Convert.ToInt32(tempArray[i])); 
      } 
     } 
     int[] tempIntArray = tempList.ToArray(); 
     MyObject = new MyObject(tempArray[0], tempIntArray[0], tempIntArray[1], tempIntArray[2], tempIntArray[3]); 
     this.MyList.Add(MyObject); 

     Console.WriteLine("MyList has " + MyList.Count.ToString() + " elements."); 
     //foreach (MyObject item in MyList) 
     //{ 
     // Console.WriteLine(item.Location); 
     //} 
    } 
} 

}

Boostrapper:

namespace MyApp 
{ 
    internal class AppBootStrapper : Bootstrapper<IShell> 
    { 
     static AppBootStrapper() 
     { 
      //Initializes the logger for debugging, remove or comment out in release. 
      LogManager.GetLog = type => new DebugLogger(type); 
     } 
     private CompositionContainer container; 
     protected override void BuildUp(object instance) 
     { 
      this.container.SatisfyImportsOnce(instance); 
     } 
     protected override void Configure() 
     { 
      this.container = 
       new CompositionContainer(
        new AggregateCatalog(
         AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>())); 
      var batch = new CompositionBatch(); 
      batch.AddExportedValue<IWindowManager>(new WindowManager()); 
      batch.AddExportedValue<IEventAggregator>(new EventAggregator()); 
      batch.AddExportedValue(this.container); 
      this.container.Compose(batch); 
     } 
     protected override IEnumerable<object> GetAllInstances(Type serviceType) 
     { 
      return this.container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType)); 
     } 
     //This method is required for the BootStrapper.cs to be discovered. 
     protected override object GetInstance(Type serviceType, string key) 
     { 
      string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key; 
      IEnumerable<object> exports = this.container.GetExportedValues<object>(contract); 
      if (exports.Count() > 0) 
      { 
       return exports.First(); 
      } 
      throw new Exception(string.Format("Could not locate any instances of contract {0}.", contract)); 
     } 
    } 
} 

Основываясь на моем понимании Caliburn.Micro, после обновления ObservableCollection MyList (а новый em добавлено), datagrid с x: name MyList должен быть обновлен. Даже без шаблона данных я бы подумал, что я увижу список пустых записей, эквивалентных по длине количеству объектов в MyList. Когда я использую этот же код в MainViewModel, а не в usercontrol, привязанный к MainView, у меня нет проблем с отображением списка. Кажется, что мне не хватает чего-то об обновлении вида в представлении.

Следует отметить, что я могу проверить, что в списке есть объекты в нем с помощью Console.WriteLine (MyList.Count.ToString()) и просмотра выходного окна. Я ненавижу спрашивать об этих вещах, каждый раз, когда я это делаю, это опечатка или что-то в равной степени глупо, но я слишком долго застрял здесь.

ПРИМЕЧАНИЕ: Даже с включенным MyList.Refresh() на каждой итерации изменений в datagrid не происходит.

ПРИМЕЧАНИЕ: Похоже, что это может ответить на мой вопрос, но я не понимаю, как его реализовать. Возможно, если кто-то еще лучше это понимает, они могут поместить строки кода в соответствующие места в моем коде и объяснить, почему это работает. Заранее спасибо. Caliburn.Micro convention-based bindings not working in nested views?

+0

Чтобы дать соответствующий контекст: Я использовал C# .NET, так 1.1, но никогда не WPF. Если вы застряли, попробуйте это: установите ** очень простое ** демо-приложение в качестве доказательства концепции. Чем меньше кода, тем лучше. Вы можете найти ответ на свой вопрос. Просто мысль. – Pressacco

+0

@Pressaco, я ценю совет. Это, по сути, расширенная версия простого демонстрационного приложения. Моя первая версия была функциональной демонстрацией, которую я заимствовал, а затем расширился. В этой итерации я пытаюсь реализовать несколько представлений, чтобы я мог постепенно добавлять fucntionality в представления, не беспокоясь о влиянии на приложение в целом. – spugm1r3

+0

Чтобы дать соответствующий контекст, я использовал C# около 6 месяцев, wpf около 3 и Caliburn.Micro в течение нескольких недель :) – spugm1r3

ответ

4

Попробуйте этот первый подход к viewmodel - я подозреваю, что ваш внутренний вид не связан (CM не просматривает границы управления при применении соглашений, например.оно не будет применять соглашения к вложенному UserControls)

<Window x:Class="MyApp.MainWindowView" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:cal="http://www.caliburnproject.org" 
    xmlns:views="clr-namespace:MyApp" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindowView" Height="300" Width="300"> 
<Grid> 
    <DockPanel> 
     <Menu DockPanel.Dock="Top"> 
      <MenuItem Header="File"> 
       <MenuItem Header="Open" x:Name="Open"/> 
       <MenuItem Header="Exit" x:Name="Exit"/> 
      </MenuItem> 
     </Menu> 
     <StackPanel DockPanel.Dock="Bottom"> 
      <!-- Use ContentControl for sub-views, CM will do it's magic if you bind to the VM property using the standard conventions --> 
      <ContentControl x:Name="SecondView" /> 
     </StackPanel> 
    </DockPanel> 
</Grid> 

Затем в главном:

internal class MainWindowViewModel : Screen, IShell 
{ 
    Regex expression = new Regex(@"^N\d\.C\d\.D\d\.R\d:\s\s\s-\d"); //ex. "N1.C1.D2.R1: -3"  
    // Declare your second VM as a property so you can bind to it via CM conventions 
    public SecondViewModel SecondView 
    { 
     get { return _secondView; } 
     set 
     { 
      _secondView = value; 
      NotifyOfPropertyChange(() => SecondView); 
     } 
    } 

    public MainWindowViewModel() 
    { 
     SecondView = new SecondViewModel(); 
    } 

CM будет автоматически вводить правильный вид в содержание элементов управления шаблоном и настройка DataContext

В качестве альтернативы , вы можете использовать Bind.Model, чтобы связать экземпляр виртуальной машины с видом, который больше подходит для просмотра.

<StackPanel DockPanel.Dock="Bottom"> 
     <views:SecondView cal:Bind.Model="{Binding SecondView}" /> 
    </StackPanel> 

(я думаю, что это Bind.Model и не View.Model, но я часто получаю два перепутаны, так не суметь Bind.Model попробовать View.Model)

+0

Я дал это, но он достигает того же результата, что и раньше. Столбцы отображаются в datagrid с заголовками, отражающими их соответствующие свойства, но строки никогда не заполняются. Кажется, что SecondView только обновляет свой внешний вид, когда он загружается, а не делает это всякий раз, когда базовые данные изменяются. – spugm1r3

+0

Проверьте мой последний комментарий к своему вопросу, единственное, что я могу придумать, это то, что у вас есть два экземпляра SecondViewModel - это может объяснить это ... как у вас есть настройка MEF для этого? – Charleh

+0

Я добавил код для загрузчика. Я копаю свой код прямо сейчас, чтобы убедиться, что я имею дело с одним и тем же экземпляром второго представления. – spugm1r3