** Update: also look at an alternative solution Shawn Kendrot did here: http://visuallylocated.com/post/2015/02/20/Creating-a-WrapPanel-for-your-Windows-Runtime-apps.aspx
Sometimes you like to present a list of items to the user. But the requirements for that list are so, that it must remain compact, wrap at the screen edge and editable in that sense that you can remove items quickly.
This kind of list is often represented by tags, so how do you do this in an universal app, let me show you how!
First thing that is very important to note, is the fact that we will be using a RichTextBlock with an InlineUIContainer. We do this, because the RichTextBlock handles the screen wrapping/overflow! That will enable us to add tags and each time a new one is added to the list, the RichtTextBlock will validate if it will still fit the current line, if not it will add a new line and wrap.
And to be honest that’s it! Only thing left to do is add the real tags, that are actual Buttons inside the InlineUIContainer. In the current demo application I’m doing this in the code behind of the MainPage file, there is a method called SetTags.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var tagParagraph = (Paragraph) (from paragraph in TagRichTextBlock.Blocks where paragraph.Name.StartsWith("Tags") select paragraph).FirstOrDefault(); var tagIds = from tag in General.GetInstance().TagSelection.Tags select tag.Id; var buttonsToRemove = from item in tagParagraph.Inlines.Cast<InlineUIContainer>() where !tagIds.Contains(((Button) item.Child).Name) select item; foreach (InlineUIContainer container in buttonsToRemove) tagParagraph.Inlines.Remove(container); |
In that method we will first go through the newly selected Tags and search for the corresponding buttons inside the RichTextBlock, when found we remove them.
After that we will be adding all new Tags.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
var buttonIds = from item in tagParagraph.Inlines.Cast<InlineUIContainer>() select ((Button) item.Child).Name; var tagsToAdd = from item in General.GetInstance().TagSelection.Tags where !buttonIds.Contains(item.Id) select item; foreach (Tag tag in tagsToAdd) { var container = new InlineUIContainer(); RichTextBlock inlineRichTextBlock = new RichTextBlock() {IsTextSelectionEnabled = false}; Paragraph inlineParagraph = new Paragraph(); inlineParagraph.Inlines.Add(new Run() {Text = string.Format("{0} ", tag.Label), FontSize = 14}); inlineParagraph.Inlines.Add(new Run() { Text = "\uE106", FontFamily = new FontFamily("Segoe UI Symbol"), FontSize = 10 }); inlineRichTextBlock.Blocks.Add(inlineParagraph); var tagButton = new Button() { Content = inlineRichTextBlock, Style = (Style)Application.Current.Resources["TagButtonStyle"], Name = tag.Id }; tagButton.Click += OnTagButtonClicked; container.Child = tagButton; tagParagraph.Inlines.Add(container); |
Again a bit the same concept as the removal. For each tag not yet in the current tag list, we create a new button with a specific style and add a click handler that will enable the removal. Once that button is created we add it to the RichTextBlock InlineUIContainer so it will show up on screen.
When the user presses the tag, the click event will fire an the selected tag will be removed from the list.
1 2 3 4 5 6 |
private void OnTagButtonClicked(object sender, RoutedEventArgs e) { var tagId = ((Button) sender).Name; General.GetInstance().TagSelection.Tags.Remove(General.GetInstance().TagSelection.Tags.Single(item => item.Id.Equals(tagId))); this.SetTags(); } |
The actual thing missing from the current implementation, is the fact that it’s not an real control yet… so if anyone feels the urge, please be my guest 🙂
Soure code can be found on my GitHub here…
This is how it looks!