2017-07-02 82 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模式中使用雙向綁定。 –