0

I have a "console emulation" as it is shown here: https://stackoverflow.com/a/14957478/6518526

There is no interactivity in my console, so it is really just a buffer that is being displayed.

So there is a ScrollViewer containing an ItemsControl, which contains TextBlocks's. The ItemsControl gets its data through a data-binding to an observable queue containing strings.

How can this setup be extended, so it is possible to select/copy text like in a console?

I have already tried to use a TextBox instead of the TextBlock, but that just gives me the possibility to select within each item, but not over several of the items at the same time.

I also found this solution here, which implements a way to populate Controls within a FlowDocument using data-bindings: https://msdn.microsoft.com/en-us/magazine/dd569761.aspx . But as it seems, also that approach won't give me selectability over several items.

So, I am little lost about which Control can be used in what way to achieve this.

Community
  • 1
  • 1
ptair
  • 177
  • 1
  • 11
  • Do you need to select part of one item, all of the next, and part of the one after? Or can you live with selecting items discretely, each item either entirely selected or entirely not? If the former, you need a multi-line readonly TextBox (with code in your viewmodel to Join the selection of lines and expose the result as a property). If the latter, I suggest a ListBox. In fact, the join/multiline-textbox thing is by far the easiest solution: Once you join the desired lines, all the rest is free. – 15ee8f99-57ff-4f92-890c-b56153 Jun 29 '16 at 17:03
  • Desirable is to be able to make arbitrary selections, so it seems the join/multiline-textbox thing is the way to go. Can you elaborate on how you meant the part with joining the selection of lines/expose as a property? – ptair Jun 29 '16 at 19:41
  • I just meant that if you have, say `public ObservableCollection Lines { ... }`, you could also have `public String Text { get { return String.Join("\n", Lines); } }` with appropriate raising of `PropertyChanged` when `Lines` changes. Then in the XAML `` with appropriate other attributes on the textbox for multiline-ness, etc. etc. – 15ee8f99-57ff-4f92-890c-b56153 Jun 29 '16 at 19:43
  • Ok, now it is clear what you meant. I was actually worried about performance going such a way, but it turns out to be no problem for the load I have. Could you put your comment into an answer so I can accept it? – ptair Jun 30 '16 at 09:21

1 Answers1

0

If you want to let the user select and copy an arbitrary contiguous subset of characters from multiple lines of text, the simplest solution is a multi-line TextBox. In your case, you'd make it read-only.

This will bog down if you've got a large enough collection of lines, but for manageable amounts of text it ought to be fine. See how it goes with the cases you're working with.

If you want to maintain your text as a collection if discreet lines (which sounds like a good idea in this case), you can simply write a property that joins the lines into a single string whenever the Lines collection changes:

C#

public MyViewModel()
{
    Lines = new ObservableCollection<String>();
}

private ObservableCollection<String> _lines;
public ObservableCollection<String> Lines
{
    get { return _lines; }
    protected set {
        _lines = value;
        UpdateText();
        _lines.CollectionChanged += _lines_CollectionChanged;
        OnPropertyChanged("Lines");
    }
}

private void UpdateText()
{
    Text = String.Join("\n", Lines);
}

private void _lines_CollectionChanged(object sender, 
    System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
    UpdateText();
}

private string _text;
public String Text {
    get { return _text; }
    protected set {
        _text = value;
        OnPropertyChanged("Text");
    }
}

XAML

<Grid>
    <TextBox 
        Text="{Binding Text}" 
        TextWrapping="Wrap"
        AcceptsReturn="True"
        IsReadOnly="True"
        />
</Grid>

You don't even need to write your own context menu.