2017-07-02 91 views
-1

我使用自定义WindowsFormsHost控件在选项卡控件中包装FastColoredTextbox winforms控件,当用户通过DataTemplate点击新文档按钮时,其选项卡动态创建。自定义控件绑定不会更新模型

由于在此SO question中解释的选项卡控制的限制,我的撤消/重做逻辑保留了每个选项卡的所有选项卡的历史记录,导致显示错误的文本。

因此,我决定修改FastColoredTextbox控件以显示历史记录和重做堆栈,并将它们添加到我自定义的WindowsFormsHost包装器控件中,作为依赖性属性,这些属性通过两种方式绑定到我的DocumentModel。

XAML:

<TabControl TabStripPlacement="Top" Margin="5" ItemsSource="{Binding Documents}" SelectedItem="{Binding CurrentDocument, Mode=TwoWay}" x:Name="TabDocuments"> 
    <TabControl.ItemTemplate> 
     <DataTemplate> 
      <Grid> 
       <Grid.ColumnDefinitions> 
        <ColumnDefinition Width="*"/> 
        <ColumnDefinition Width="10" MaxWidth="10" MinWidth="10"/> 
        <ColumnDefinition Width="10" MaxWidth="10" MinWidth="10"/> 
       </Grid.ColumnDefinitions> 
       <TextBlock Grid.Column="0" Text="{Binding Metadata.FileName}"/> 
       <TextBlock Grid.Column="1" Text="*" Visibility="{Binding IsSaved, Converter={StaticResource VisibilityConverter}}"/> 
       <Button Grid.Column="2" Width="10" Height="10" MaxWidth="10" MaxHeight="10" MinWidth="10" MinHeight="10" VerticalAlignment="Center" HorizontalAlignment="Right" Command="{Binding CloseDocumentButtonCommand}"> 
        <TextBlock Text="X" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="8" FontWeight="Bold"/> 
       </Button> 
      </Grid> 
     </DataTemplate> 
    </TabControl.ItemTemplate> 
    <TabControl.ContentTemplate> 
     <DataTemplate x:Name="TabDocumentsDataTemplate"> 
      <Grid> 
       <Grid.ColumnDefinitions> 
        <ColumnDefinition Width="*"/> 
        <ColumnDefinition Width="*"/> 
       </Grid.ColumnDefinitions> 
       <Border BorderBrush="Black" BorderThickness="1" Grid.Column="0" Margin="5"> 
        <customControls:CodeTextboxHost Text="{Binding Markdown, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" History="{Binding History, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" RedoStack="{Binding RedoStack, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" WordWrap="True" x:Name="CodeTextboxHost"/> 
       </Border> 
       <Border BorderBrush="Black" BorderThickness="1" Grid.Column="1" Margin="5"> 
        <wpf:ChromiumWebBrowser Address="{Binding Html, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:Name="ChromiumWebBrowser"/>  
       </Border> 
      </Grid> 
     </DataTemplate> 
    </TabControl.ContentTemplate> 
</TabControl> 

模型的相关部分:

public ObservableCollection<UndoableCommand> History 
{ 
    get { return _history; } 
    set 
    { 
     _history = value; 
     OnPropertyChanged(); 
    } 
} 

public ObservableCollection<UndoableCommand> RedoStack 
{ 
    get { return _redoStack; } 
    set 
    { 
     _redoStack = value; 
     OnPropertyChanged(); 
    } 
} 

自定义控制代码的相关部分:

public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CodeTextboxHost), new PropertyMetadata("", new PropertyChangedCallback(
(d, e) => 
{ 
    var textBoxHost = d as CodeTextboxHost; 
    if (textBoxHost != null && textBoxHost._innerTextbox != null) 
    { 
     textBoxHost._innerTextbox.Text = textBoxHost.GetValue(e.Property) as string; 
    } 
}), null)); 

public static readonly DependencyProperty HistoryProperty = DependencyProperty.Register("History", typeof(LimitedStack<UndoableCommand>), typeof(CodeTextboxHost), new PropertyMetadata(new LimitedStack<UndoableCommand>(200), new PropertyChangedCallback(
    (d, e) => 
    { 
     var textBoxHost = d as CodeTextboxHost; 
     if (textBoxHost != null && textBoxHost._innerTextbox != null) 
     { 
      var history = textBoxHost.GetValue(e.Property) as LimitedStack<UndoableCommand>; 
      if (history != null) 
      { 
       textBoxHost._innerTextbox.TextSource.Manager.History = history; 
       textBoxHost._innerTextbox.OnUndoRedoStateChanged(); 
      } 
      else 
      { 
       textBoxHost._innerTextbox.ClearUndo(); 
      } 
     } 
    }), null)); 

public static readonly DependencyProperty RedoStackProperty = DependencyProperty.Register("RedoStack", typeof(Stack<UndoableCommand>), typeof(CodeTextboxHost), new PropertyMetadata(new Stack<UndoableCommand>(), new PropertyChangedCallback(
    (d, e) => 
    { 
     var textBoxHost = d as CodeTextboxHost; 
     if (textBoxHost != null && textBoxHost._innerTextbox != null) 
     { 
      var redoStack = textBoxHost.GetValue(e.Property) as Stack<UndoableCommand>; 
      if (redoStack != null) 
      { 
       textBoxHost._innerTextbox.TextSource.Manager.RedoStack = redoStack; 
       textBoxHost._innerTextbox.OnUndoRedoStateChanged(); 
      } 
      else 
      { 
       textBoxHost._innerTextbox.ClearUndo(); 
      } 
     } 
    }), null)); 

public LimitedStack<UndoableCommand> History 
{ 
    get { return (LimitedStack<UndoableCommand>) GetValue(HistoryProperty);} 
    set { SetValue(HistoryProperty, value);} 
} 

public Stack<UndoableCommand> RedoStack 
{ 
    get { return (Stack<UndoableCommand>) GetValue(RedoStackProperty); } 
    set { SetValue(RedoStackProperty, value);} 
} 

public CodeTextboxHost() 
{ 
    Child = _innerTextbox; 
    _innerTextbox.Language = FastColoredTextBoxNS.Language.Custom; 
    _innerTextbox.DescriptionFile = AppDomain.CurrentDomain.BaseDirectory + "SyntaxConfig\\MarkdownSyntaxHighlighting.xml"; 
    _innerTextbox.HighlightingRangeType = HighlightingRangeType.AllTextRange; 
    _innerTextbox.TextChanged += _innerTextbox_TextChanged; 
} 

private void _innerTextbox_TextChanged(object sender, TextChangedEventArgs e) 
{ 
    Text = _innerTextbox.Text; 
    History = _innerTextbox.TextSource.Manager.History; 
    RedoStack = _innerTextbox.TextSource.Manager.RedoStack; 
} 

我的问题是,当TextChanged事件触发,HistoryRedoStack不更新模型,而Text完美更新。

我试着添加RelativeSource,因为这个SO question暗示,但它没有奏效。

任何帮助/想法,将不胜感激。谢谢。

编辑1:

至于建议我做了所有集合的ObservableCollection,但它没有做任何改变,因为该模型再次没有更新。

public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CodeTextboxHost), new PropertyMetadata("", new PropertyChangedCallback(
    (d, e) => 
    { 
     var textBoxHost = d as CodeTextboxHost; 
     if (textBoxHost != null && textBoxHost._innerTextbox != null) 
     { 
      textBoxHost._innerTextbox.Text = textBoxHost.GetValue(e.Property) as string; 
     } 
    }), null)); 

public static readonly DependencyProperty HistoryProperty = DependencyProperty.Register("History", typeof(ObservableCollection<UndoableCommand>), typeof(CodeTextboxHost), new PropertyMetadata(new ObservableCollection<UndoableCommand>(), new PropertyChangedCallback(
    (d, e) => 
    { 
     var textBoxHost = d as CodeTextboxHost; 
     if (textBoxHost != null && textBoxHost._innerTextbox != null) 
     { 
      var history = textBoxHost.GetValue(e.Property) as ObservableCollection<UndoableCommand>; 
      if (history != null) 
      { 
       textBoxHost._innerTextbox.TextSource.Manager.History = history.ToLimitedStack(200); 
       textBoxHost._innerTextbox.OnUndoRedoStateChanged(); 
      } 
      else 
      { 
       textBoxHost._innerTextbox.ClearUndo(); 
      } 
     } 
    }), null)); 

public static readonly DependencyProperty RedoStackProperty = DependencyProperty.Register("RedoStack", typeof(ObservableCollection<UndoableCommand>), typeof(CodeTextboxHost), new PropertyMetadata(new ObservableCollection<UndoableCommand>(), new PropertyChangedCallback(
    (d, e) => 
    { 
     var textBoxHost = d as CodeTextboxHost; 
     if (textBoxHost != null && textBoxHost._innerTextbox != null) 
     { 
      var redoStack = textBoxHost.GetValue(e.Property) as ObservableCollection<UndoableCommand>; 
      if (redoStack != null) 
      { 
       textBoxHost._innerTextbox.TextSource.Manager.RedoStack = redoStack.ToStack(); 
       textBoxHost._innerTextbox.OnUndoRedoStateChanged(); 
      } 
      else 
      { 
       textBoxHost._innerTextbox.ClearUndo(); 
      } 
     } 
    }), null)); 

public ObservableCollection<UndoableCommand> History 
{ 
    get { return (ObservableCollection<UndoableCommand>) GetValue(HistoryProperty);} 
    set { SetValue(HistoryProperty, value);} 
} 

public ObservableCollection<UndoableCommand> RedoStack 
{ 
    get { return (ObservableCollection<UndoableCommand>) GetValue(RedoStackProperty); } 
    set { SetValue(RedoStackProperty, value);} 
} 

public string Text 
{ 
    get { return (string)GetValue(TextProperty); } 
    set { SetValue(TextProperty, value); } 
} 

public bool WordWrap 
{ 
    get { return (bool)GetValue(WordWrapProperty); } 
    set { SetValue(WordWrapProperty, value); } 
} 

public CodeTextboxHost() 
{ 
    Child = _innerTextbox; 
    _innerTextbox.Language = FastColoredTextBoxNS.Language.Custom; 
    _innerTextbox.DescriptionFile = AppDomain.CurrentDomain.BaseDirectory + "SyntaxConfig\\MarkdownSyntaxHighlighting.xml"; 
    _innerTextbox.HighlightingRangeType = HighlightingRangeType.AllTextRange; 
    _innerTextbox.TextChanged += _innerTextbox_TextChanged; 
} 

private void _innerTextbox_TextChanged(object sender, TextChangedEventArgs e) 
{ 
    Text = _innerTextbox.Text; 
    History = _innerTextbox.TextSource.Manager.History.ToOveObservableCollection(); 
    RedoStack = _innerTextbox.TextSource.Manager.RedoStack.ToObservableCollection(); 
} 
+0

如果我猜的话,我会说这个问题与您在模型中使用'ObservableCollection'的事实开始的地方做,依赖项属性中的'Stack'和'LimitedStack'。当这些匹配时,我只有绑定集合的好运气。我全部使用'ObservableCollection'。 –

+0

我无法弄清楚完整的代码,但是如果你想明确地调用更新,就有解决方法。尝试交互触发TextBox文本更改它将调用带参数的命令,然后您可以记录您的更改DS – Ramankingdom

+0

@Ramankingdom我实际上使用它,唯一的区别是,文本框本身是一个winforms控件,我用它包装'WindowsFormsHost'。我使用内部文本框的TextChanged事件来更新文本,历史记录和重做堆栈(可以在'_innerTextbox_TextChanged'方法中看到)。但是,问题只是文本更新,其余部分不会更新并存储回模型中。 –

回答

0

我能够使UI通过修改自定义的控制代码更改模型如下:

public static readonly DependencyProperty HistoryProperty = DependencyProperty.Register("History", typeof(IEnumerable<UndoableCommand>), typeof(CodeTextboxHost), new FrameworkPropertyMetadata(new ObservableCollection<UndoableCommand>(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(
    (d, e) => 
    { 
     var textBoxHost = d as CodeTextboxHost; 
     if (textBoxHost != null && textBoxHost._innerTextbox != null) 
     { 
      var history = textBoxHost.GetValue(e.Property) as ObservableCollection<UndoableCommand>; 
      if (history != null) 
      { 
       textBoxHost._innerTextbox.TextSource.Manager.History = history.ToLimitedStack(200); 
       textBoxHost._innerTextbox.OnUndoRedoStateChanged(); 
      } 
      else 
      { 
       textBoxHost._innerTextbox.ClearUndo(); 
      } 
     } 
    }), null)); 

public static readonly DependencyProperty RedoStackProperty = DependencyProperty.Register("RedoStack", typeof(IEnumerable<UndoableCommand>), typeof(CodeTextboxHost), new FrameworkPropertyMetadata(new ObservableCollection<UndoableCommand>(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(
    (d, e) => 
    { 
     var textBoxHost = d as CodeTextboxHost; 
     if (textBoxHost != null && textBoxHost._innerTextbox != null) 
     { 
      var redoStack = textBoxHost.GetValue(e.Property) as ObservableCollection<UndoableCommand>; 
      if (redoStack != null) 
      { 
       textBoxHost._innerTextbox.TextSource.Manager.RedoStack = redoStack.ToStack(); 
       textBoxHost._innerTextbox.OnUndoRedoStateChanged(); 
      } 
      else 
      { 
       textBoxHost._innerTextbox.ClearUndo(); 
      } 
     } 
    }), null)); 

public ObservableCollection<UndoableCommand> History 
{ 
    get { return (ObservableCollection<UndoableCommand>) GetValue(HistoryProperty);} 
    set { SetCurrentValue(HistoryProperty, new ObservableCollection<UndoableCommand>(value));} 
} 

public ObservableCollection<UndoableCommand> RedoStack 
{ 
    get { return (ObservableCollection<UndoableCommand>) GetValue(RedoStackProperty); } 
    set { SetCurrentValue(RedoStackProperty, new ObservableCollection<UndoableCommand>(value));} 
} 

所以不是SetValue,我用SetCurrentValueHistoryRedoStack使UI更新模型正确。

0

使用INotifyPropertyChanged接口和实现OnPropertyChanged方法,并将该方法要更新UI

+0

也许一个更好的答案的链接将有所帮助:https://stackoverflow.com/questions/7934236/inotifypropertychanged-and-observablecollection-wpf – Digvijay

+0

我不想更新用户界面,目前的数据绑定工程从模型到UI很好。问题是,当我在可观察集合中进行UI更新时,它不更新模型的可观察集合。 –

+0

在您的xaml模式中使用双向绑定。 –