Combine TextBoxes into one from an ItemsSource - c#

Is there a way to combine all the textboxes that this itemscontrol creates so I can select them all as in a single textbox or textblock? As of right now, I can only select the text in a single textblock at a time.
<ItemsControl Background="WhiteSmoke" ItemsSource="{Binding SelectedItemNotes}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBox IsReadOnly="True" Background="Transparent" BorderThickness="0">
<TextBox.Text>
<MultiBinding StringFormat="{}{0} {1} - {3}">
<Binding Path="Timestamp" />
<Binding Path="UserName" />
<Binding Path="Notes" />
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

So as far as text selection interaction goes, you want the user to interact with the text in multiple text boxes it as if it were all in one big multi-line textbox: User clicks between the fourth and fifth characters in the first box, drags to just before the ninth character in the third box, and releases the left mouse button. The result is that everything after the fourth character in the first text box is selected, all of the second text box is selected, and the first eight characters of the third are selected.
There's nothing that'll do that for you. You'd be writing custom mouse interaction. I've done text selection with a mouse, though only in a single control, and it's finickier than you realize, but it's pretty awesome when it's finally working right.
So you'd set up some private state in your control class so the mouse events know that the user is engaged in this mega-selection interaction. Mousedown and drag in a textbox in the ItemsControl would launch that, mouseup in one would end it.
On mousemove, you'll have to set SelectionStart/SelectionEnd for each of the textboxes between the one you started at, and the one you're over. For the one you're over (if it's not the first one), you'll have to do custom code to find out which character the mouse is over, and programmatically set SelectionStart and SelectionEnd.
Don't forget that the user can drag-select "upward" from TextBox 2 to TextBox 0 you need to know which way the selection is going.
Once you know which textboxes are in your selection, write a property that enumerates those. Given that, once they have the appropriate text selected, it's easy to get the full selection text:
// In order of first in UI to last, regardless of "selection direction"
protected IEnumerable<TextBox> MegaSelectionTextBoxes {
get {
// Whatever -- might be a good idea to keep them
// in a List<TextBox> or ObservableCollection<TextBox>
}
}
// The easy part
public String MegaSelectionText {
get {
return
String.Join("\n",
MegaSelectionTextBoxes.Select(
tb=>tb.SelectedText));
}
}
Myself, I wouldn't count on getting that working really right in less than a week. Your definition of "right" may be a lot more permissive though.
But I would prefer an approach that works with XAML rather than trying to work around it.
If it were me, unless this feature was really critical for some reason (and sometimes they are), I'd make a case to sacrifice the partial selection in the first and last textboxes in the selection.
I'd look at using a multiselect ListBox rather than an ItemsControl, so I could let the user select items. Then I'd bind SelectedItems on the ListBox to an ObservableCollection on my viewmodel, and write a Command on my viewmodel that does whatever I want with the concatenated text of all the selected items.
Another option is use a DataGrid which multiple cell selection. That'll get you the interaction you want, though you may have to play some games to get it to look the way you want it to.

Related

Highlight part of text in textblock not working on all listboxItems

I'm trying to highlight part of text in a textblock from a listbox datatemplate which in turn is bounded to a property of a custom class by using a textbox to search the list for input text. But the problem is that only part of the items are highlighting (most of the ones visible) but when i maximize the window and try to input another character then suddenly all of them gets highlighted my guess where the problem might be is in this piece of code:
ListBoxItem listboxItemFound= (ListBoxItem)this.listBox1.ItemContainerGenerator.ContainerFromItem(TItem);
Since this method is returning a null when the items are not visible but the items are currently in the listbox. Somehow I guess the items listboxItem instances are not yet created until you scroll down or maximize to view more items.
XAML DataTemplate:
<DataTemplate>
<Grid Name="gridOfListbox" Height="25" Margin="0,2">
<DockPanel Name="dockpanelWithTxtBlock">
<TextBlock Name="textbloxk" DockPanel.Dock="Left" FontSize="15" TextAlignment="Center">
<Run Text="" /><Run Background="Yellow" Text="" /><Run Text="{Binding ProductID}" />
</TextBlock>
</DockPanel>
</Grid>
</DataTemplate>
If more code is needed just let me know.
Any help would be greatly appreciated!!
Also if there is any other better way of finding the listboxItem bounded to the custom Item just let me know. Thank you very much!
[Pic of problem] http://i.stack.imgur.com/HViag.png
One way to fix this is to set VirtualizingStackPanel.IsVirtualizing to false for your ListBox. This will cause all of the items to be created right away. The downside to this is if your ListBox has many items, your program will use more memory (since more items will be created), and could potentially run slower depending on the number of items.
A better solution to consider would be to have multiple DataTemplates for this - one without the highlight, and one with. You can set a DataTemplateSelector for your ListBox (using the ItemTemplateSelector property). The selector can choose which template to use based on if the item matches the search term or not.
The tricky part would be writing the template with the highlighted text. You could probably achieve that by having properties on the object the ListBoxItem is bound to for the text before the highlighted text, the highlighted text, and then the remaining text.

WPF: Which solution? TabControl with close button and new tab button

I'm trying to find the best solution for a TabControl that both support a close button on each TabItem, and always show a "new tab button" as the last tab.
I've found some half working solutions, but i think that was for MVVM, that I'm not using. Enough to try to understand WPF =)
This is the best solution I've found so far:
http://www.codeproject.com/Articles/493538/Add-Remove-Tabs-Dynamically-in-WPF
A solution that i actually understand. But the problem is that it is using the ItemsSource, and i don't want that. I want to bind the ItemsSource to my own collection without having to have special things in that collection to handle the new tab button.
I've been search for days now but cant find a good solution.
And I'm really new to WPF, otherwise i could probably have adapted the half done solutions I've found, or make them complete. But unfortunately that is way out of my league for now.
Any help appreciated.
I have an open source library which supports MVVM and allows extra content, such as a button to be added into the tab strip. It is sports Chrome style tabs which can tear off.
http://dragablz.net
This is bit of a dirty way to achieve the Add (+) button placed next to the last TabItem without much work. You already know how to place a Delete button next to the TabItem caption so I've not included that logic here.
Basically the logic in this solution is
To bind ItemsSource property to your own collection as well as
the Add TabItem using a CompositeCollection.
Disable selection of
the Add(+) TabItem and instead perform an action to load a new tab when it
is clicked/selected.
XAML bit
<TextBlock x:Name="HiddenItemWithDataContext" Visibility="Collapsed" />
<TabControl x:Name="Tab1" SelectionChanged="Tab1_SelectionChanged" >
<TabControl.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding DataContext.MyList, Source={x:Reference HiddenItemWithDataContext}}" />
<TabItem Height="0" Width="0" />
<TabItem Header="+" x:Name="AddTabButton"/>
</CompositeCollection>
</TabControl.ItemsSource>
</TabControl>
The code behind
private void Tab1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Contains(AddTabButton))
{
//Logic for adding a new item to the bound collection goes here.
string newItem = "Item " + (MyList.Count + 1);
MyList.Add(newItem);
e.Handled = true;
Dispatcher.BeginInvoke(new Action(() => Tab1.SelectedItem = newItem));
}
}
You could make a converter which appends the Add tab. This way the collection of tabs in you viewmodel will only contain the real tabs.
The problem is then how to know when the Add tab is selected. You could make a TabItem behavior which executes a command when the tab is selected. Incidentally I recommended this for another question just recently, so you can take the code from there: TabItem selected behavior
While I don't actually have the coded solution, I can give some insight on what is most likely the appropriate way to handle this in a WPF/MVVM pattern.
Firstly, if we break down the request it is as follows:
You have a sequence of elements that you want to display.
You want the user to be able to remove an individual element from the sequence.
You want the user to be able to add a new element to the sequence.
Additionally, since you are attempting to use a TabControl, you are also looking to get the behavior that a Selector control provides (element selection), as well as an area to display the element (content) which is selected.
So, if we stick to these behaviors you'll be fine, since the user interface controls can be customized in terms of look and feel.
Of course, the best control for this is the TabControl, which are you already trying to use. If we use this control, it satisfies the first item.
<TabControl ItemsSource="{Binding Path=Customers}" />
Afterwards, you can customize each element, in your case you want to add a Button to each element which will execute a command to remove that element from the sequence. This will satisfy the second item.
<TabControl ...>
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=CustomerId}" />
<Button Command="{Binding Path=RemoveItemCommand, Mode=OneTime,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type TabControl}}"
CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
<TabControl.ItemTemplate>
</TabControl>
The last part is a bit more difficult, and will require you to actually have to create a custom control that inherits from the TabControl class, add an ICommand DependencyProperty, and customize the control template so that it not only displays the TabPanel, but right next to it also displays a Button which handles the DependencyProperty you just created (the look and feel of the button will have to be customized as well). Doing all of this will allow you to display your own version of a TabControl which has a faux TabItem, which of course is your "Add" button. This is far far far easier said than done, and I wish you luck. Just remember that the TabPanel wraps onto multiple rows and can go both horizontally or vertically. Basically, this last part is not easy at all.

wpf combobox default behaviour

I have a combobox in a wpf c# application.
What i am trying to do is the following.
I have a unselected combobox, as you look at it i can see an arrow to the right hand side and a space for text on the left. For the purpose of this question i'll refer to this text as 'Cell Text'.
When you select the button it appears with a list. I want this list to contain a number of robots my GUI/PC can connect to. When i select a robot, a message is sent to this robot trying to connect with it.
The 'Cell Text' i want to display the name of the currently connected Robot. There will be situations when a connection to a selected robot would'nt be possible, also a successful connection could take 5 seconds.
What i need to do is stop the selection automatically appearing in the 'Cell Text', is this possible?
Thanks
<ComboBox ItemsSource="{Binding MyRobotOptions}" Grid.Column="1" SelectedItem="{Binding SelectedRobot}" Margin="5"/>
For an inexperienced user (no offence), one of the easiest ways you can do this is to overlay a TextBlock over the 'Cell Text', as you call it:
<Grid>
<ComboBox ItemsSource="{Binding MyRobotOptions}" Grid.Column="1" SelectedItem="{
Binding SelectedRobot}" Margin="5" />
<TextBlock Text="{Binding YourSelectedRobotName}" Background="White"
Margin="0,0,24,0" />
</Grid>
I haven't been able to test this, so you might need to adjust the Margin property values to make it fit better, but it should hide the original text value.
A better solution I think is to use a separate indicator for connection state. For example a colored border around the Combobox that turns green when connected, red when disconnected. That way you don't have to break the paradigm of a ComboBox that everyone assumes: when you select something it immediately appears selected in the ComboBox.

WPF TextBox with both static and editable text?

I have a data entry application, with (among other things) a textbox for recording comments. These comments are specific to the data being entered, and often times are redundant (same comment given for a range of data records).
I'm planning to add a combobox with a canned selection of comments to cover the most common situations. When one is selected from the combobox, the comment textbox is populated with the canned comment.
However, I also need the ability to enter additional comments after the canned message, within the textbox. But I don't want it to be possible for the canned message to be altered. All entered comments need to come after the canned comment.
Is there a way to apply static text to a textbox which cannot be altered, but still allow text to be entered below it?
The only idea I've come up with so far is to catch TextInput events and continually overwrite the beginning of the textbox content with the canned message, but the result wouldn't exactly be pretty.
You could just place a disabled TextBox immediately above the editable TextBox and remove their bottom and top borders respectively so that they look like one big TextBox.
Perhaps overwrite a TextBox template so that it contains a Panel with the Canned Message ComboBox and a regular TextBox for user input.
Style the inner TextBox so it doesn't have the regular TextBox border, and style the ComboBox so that when it doesn't have focus it doesn't show it's border either.
When the ComboBox has focus, it will look like a ComboBox inside a TextBox, and if it doesn't have focus it will just look like one big TextBox
No, as much I aware of , you can not have something like this. But you can:
Have a label on top (side) of it with static text applied
If you have enough space, have a readonlt text box for preview of the comment, more or less like SO comment editor works.
You can try to not let to delete several count of characters from binded data ( which will be actually static text)
Hope this helps.
I would adopt a slightly different strategy.
If something has been selected in the Combobox, then concatenate the input in the selected combo-box item text and the textbox text. If not, use the textbox text.
Of course you could use the selected event of the combobox to chnage the label to reflect the change in circumstance.
I think you can bind the textBox1.Text with the combobox selected item as Oneway mode. The following is code snipet
<ComboBox Height="23" HorizontalAlignment="Left" Margin="118,48,0,0" Name="comboBox1" VerticalAlignment="Top" Width="144">
<ComboBoxItem Content="Commanet 1" />
<ComboBoxItem Content="Comment 2" />
</ComboBox>
<TextBox Height="64" HorizontalAlignment="Left" Margin="118,101,0,0" Name="textBox1" VerticalAlignment="Top" Width="144" Text="{Binding ElementName=comboBox1, Path=SelectedItem.Content, Mode=OneWay}" />
2 more options.
Override the style of the textbox to include the fixed text - pass the fixed text though some templatebinding
You can capture the PreviewKey<> events on the textbox and cancel it its modifying the "fixed" text, and if not let the event go through.
But 2 textboxes that visually look as 1 is still a better option though - easiest to implement and maintain

wpf updating text block

I am trying to create some form of updating area in wpf. It needs to be up datable as it will be connected to a live stream of text that will constantly need to be displayed.
The idea is that I will have a stream of data which will comprise of a UserName and Text, this will come in a random times and need to be displayed:
User:Test :: Test:TextData
User:NextTest :: Test:TestData
and so on each item on a new line, so the object needs to be up datable in a scrolling format so the new item will be added to the bottom.
Currently I am using:
<ScrollViewer VerticalScrollBarVisibility="Auto">
<StackPanel>
<TextBox Margin="5" TextWrapping="Wrap" AcceptsReturn="True" IsReadOnly="True" x:Name="LogDetails"></TextBox>
</StackPanel>
</ScrollViewer>
However this does not really show the data very well, the data is just string based, does anyone know of a better solution?
Thanks
The question is a little bit vague, but here a try:
As an option, why not using TextBlock instead of TextBox, if the text changes automatically?
Caveat: If you want to use the TextBox, don't forget to set the UndoLimit to 0 . Otherwise, you will have a lot of memory consumption, if you change the TextBox contents continously.
<TextBox UndoLimit="0" .../>
If your text is a concattenation of multiple string-elements, create a layout with a grid and use multiple TextBlocks to shown the data more nicely? Maybe there is also some data you can visualize as symbols?
If it is a log, maybe you want to fill a list with strings and set this list as the ItemsSource of an ItemsControl? Through the ItemTemplate-property you can then specify the layout of each item? Use an ObservableCollection<string>, then you only have to add the strings to the collection and the ItemsControl will refresh automatically. You can use ItemsControl, ListBox, ListView for such a log.

Categories

Resources