2017-07-02 4 views
-1

Ich verwende ein benutzerdefiniertes WindowsFormsHost Steuerelement, um FastColoredTextbox winforms-Steuerelement in einem Registersteuerelement zu verschieben, dessen Registerkarten dynamisch erstellt werden, wenn Benutzer die neue Dokumentschaltfläche über DataTemplate trifft.Benutzerdefinierte Steuerelementbindung aktualisiert das Modell nicht

Aufgrund der Einschränkungen der Registerkarte, die in diesem SO question, meine Undo/Redo-Logik hielt die Geschichte aller Registerkarten für jeden Reiter erklärt, die in falschen Text angezeigt wird resultierenden wurde.

Also entschied ich, das FastColoredTextbox-Steuerelement zu ändern, um Verlauf und Redo-Stapel verfügbar zu machen, und fügte sie meinem benutzerdefinierten WindowsFormsHost-Wrapper-Steuerelement als Abhängigkeitseigenschaften hinzu, die auf zwei Arten an mein DocumentModel gebunden ist.

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> 

Der relevante Teil des Modells:

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

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

Die relevanten Teile des benutzerdefinierten Steuercode:

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; 
} 

Mein Problem ist, wenn TextChanged Ereignis ausgelöst, History oder RedoStack nicht Update auf dem Modell, während Text perfekt aktualisiert.

Ich habe versucht, RelativeSource hinzuzufügen, wie dies SO question schlägt, aber es hat nicht funktioniert.

Jede Hilfe/Ideen würde geschätzt werden. Vielen Dank.

Edit 1:

Wie schlug ich alle Sammlungen ObservableCollection gemacht, aber es ist keine Änderung gemacht hat, wie das Modell wieder nicht aktualisiert.

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

aktualisiert werden sollen Wenn ich raten müsste würde ich sagen, das Problem mit der Tatsache ausgeht, dass Sie 'ObservableCollection' im Modell verwenden und 'Stack' und' LimitedStack' in den Abhängigkeitseigenschaften. Ich hatte nur viel Glück mit verbindlichen Sammlungen, wenn diese übereinstimmen. Ich benutze 'ObservableCollection' in allen. –

+0

Ich kann den vollständigen Code nicht herausfinden, aber es gibt Workarounds, falls Sie explizit Updates aufrufen möchten. Interaction Trigger auf TextBox Text Changed Es wird einen Befehl mit Parameter aufrufen, dann können Sie die Änderungen in Ihnen aufzeichnen DS – Ramankingdom

+0

@Ramankingdom Ich benutze das tatsächlich, der einzige Unterschied ist, dass die Textbox selbst ein winforms-Steuerelement ist, und ich wickle es mit a 'WindowsFormsHost'. Ich benutze das "TextChanged" -Ereignis des inneren Textfeldes, um den Text, den Verlauf und den Redo-Stack zu aktualisieren (wie man in der '_innerTextbox_TextChanged'-Methode sehen kann). Das Problem besteht jedoch nur in Textaktualisierungen, der Rest wird nicht aktualisiert und im Modell gespeichert. –

Antwort

0

konnte ich die Benutzeroberfläche das Modell ändert durch Ändern des benutzerdefinierten Steuercode machen, wie folgt:

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));} 
} 

habe ich also statt SetValue, SetCurrentValue auf History und RedoStack das UI-Update zu machen das Modell korrekt.

0

Verwendung INotifyPropertyChanged Schnittstelle und OnPropertyChanged Methode implementieren und diese Methode setzen, wo wollen Sie die UI

+0

Vielleicht würde ein Link zu einer besseren Antwort helfen: https://stackoverflow.com/questions/7934236/inotifypropertychanged-and-observablecollection-wpf – Digvijay

+0

Ich möchte nicht die Benutzeroberfläche aktualisieren, die aktuelle Datenbindung funktioniert vom Modell zu die Benutzeroberfläche fein. Das Problem besteht darin, dass beim Aktualisieren der Benutzeroberfläche in den beobachtbaren Sammlungen die beobachtbaren Sammlungen des Modells nicht aktualisiert werden. –

+0

Verwenden Sie die Zwei-Wege-Bindung für Ihren Modus des XAML. –

Verwandte Themen