WPF: Boldify part of binded value of TextBlock - c#

I am looking for a way to highlight some search terms inside my custom ListView control. I have a bunch of TextBlocks (one for each property of each row). For example the artist name, title and genre of each song.
Now, if someone searches for "Emi" then I want the artist field to show up like <b>Emi</b>nem, if the value of the binding is Eminem.
I have looked around a bit but didn't get much wiser. I figure I need some combination of a converter and using Inlines (which I've never used before) and/or InlineExpressions (or are those only for ASP?).
All bindings and templates are created on-the-fly in C# and not XAML.
Thanks!

Yes, you are right about using converter(actually it will may be even multiconveter) and Inlines collection of a TextBlock.
So let's say you are passing search item(in your case word 'Emi') to the converter. You will also need to manipulate the TextBlock with resulting text somehow. For simplicity let's assume that TextBlock's Tag property(not Text) contails the whole string being searched(word 'Eminem').
class HighlightPartOfTextConverter : IValueConverter {
public object Convert(object value/*this is TextBlock*/, Type type, object parameter/*this is 'Emi'*/, CultureInfo ci){
var textBlock = value as TextBlock;
string str = textBlock.Tag as string;
string searchThis = parameter as string;
int index = str.IndexOf(searchThis);
if(index >= 0){
string before = str.Substring(0, index);
string after = str.Substring(index + searchThis.Length);
textBlock.Inlines.Clear();
textBlock.Inlines.Add(new Run(){Text=before});
textBlock.Inlines.Add(new Run(){Text=searchThis, FontWeight = FontWeights.Bold});
textBlock.Inlines.Add(new Run(){Text=after});
}
return "";
}
public object ConvertBack(...) {...}
}

Related

What is a good way to set a default value in a sorted ComboBox?

When I initialize a combobox with text contents like so, where eo is some object with a ToString() override:
foreach (EncodingInfo ei in Encoding.GetEncodings()) {
Encoding e = ei.GetEncoding();
encodeObject eo = new encodeObject();
eo.Name = ei.DisplayName;
eo.Value = ei.Name;
int targetIndex = this.targetEncodingBox.Items.Add(eo);
}
I can set this to be the default value by using
this.targetEncodingBox.SelectedIndex = targetIndex
However, when the box is actually sorted, and the data initially entered into the box using the Add() method is not sorted, the default index is kept while the box is re-sorted, resulting in an entirely different value being selected almost all of the time.
A basic solution for this is to look up the generated value that the combobox would display and use FindStringExact:
this.targetEncodingBox.SelectedIndex = this.targetEncodingBox.FindStringExact("utf-8 -- Unicode (utf-8)");
However, this results in other problems. The string in question may depend on the user's operating system' language settings in this particular case. This can't be known beforehand.
Thus another way I've found is to manually find the name of the encoding a second time and set the SelectedIndex after the box is fully populated, using the same convention for concatenating the acronym name and translated name as used in the definition for encodeObject.ToString();.
foreach (EncodingInfo ei in Encoding.GetEncodings()) {
if (ei.Name == "utf-8") {
this.sourceEncodingBox.SelectedIndex = this.sourceEncodingBox.FindStringExact(ei.Name + " -- " + ei.DisplayName);
}
}
Note: the definition of the class encodeObject below:
private class encodeObject {
public string Name;
public string Value;
public override string ToString() {
return Value + " -- " + Name;
}
}
This actually works, and does exactly what I want, yet the solution seems quite clunky to do something that should really be a single call. Is there a better way of achieving this?
As Hans commented you need to create that list and store it to a variable.
Since the available encodings are unlikely to change anyway, this should happen in some class constructor or when you load your settings.
This variable then can be re-used anywhere you need it, it also can be easily updated & sorted as you like.
After this step the rest is trivial, create a variable with a default value/index, and once a ComboBox was assigned this list just set the SelectedValue/SelectedIndex value to your default value/index.

Formatting Data in MVVM within Windows Phone app

I have a DataModel and a ViewModel working successfully together with my xaml view.
In the view I use databindings.
In my DataModel I have some properties like Distance declared as int.
When displaying the values in view, I want a formatting like adding a trailing meter.
How get this done?
you can format string in the xaml binding...
<TextBlock Text="{Binding Distance, StringFormat={} {0} meter}"/>
Maybe by providing a property which will return a formatted value:
private int distance = 0;
public int Distance
{
get { return distance;}
set { distance = value; OnPropertyChanged("DistanceTxt"); }
}
public string DistanceTxt
{
get { return distance.ToString() + " meter"; }
}
Then when you bind to DistanceTxt you should get your distance with trailing meter. I've also added OnPropertyChanged in Distance property so that when it changes, your value on the screen updates.

Custom control moving another element

I'm trying to create my custom control for:
(numeric updown)
I want to use the value in the control to offset the position of a certain element (which is set using "targetElement" on the control's properties). But since settings are stored as strings, I only have the string value.
For example: If the user sets "targetElement" as label1, I want to offset the position of label1 using BobbyUpDown1.targetElement.Location.x, but since targetElement is storing a string of the ID of the element, it won't let me do that.
How can I grab the reference to the label1 (that the user input) from the targetElement property?
Do this:
Control c = this.Controls["YourLabelName"];
if (c != null)
{
c.Location = new Point(newXLocationForYourLabel, c.Location.Y);
}
Here this is the Form

Dynamically set TextBox text

I have multiple XAML TextBoxes, each of which manipulate a corresponding value in an array, when the value in the TextBox is changed, using a C# method which dynamically checks which TextBox has called the method.
<TextBox x:Name="_0_0" TextChanged="_x_y_TextChanged"/>
<TextBox x:Name="_0_1" TextChanged="_x_y_TextChanged"/>
<TextBox x:Name="_0_2" TextChanged="_x_y_TextChanged"/>
// And so on.....
each of which manipulate a corresponding value in an array, when the value in the TextBox is changed, using a C# method which dynamically checks which TextBox has called the method.
private void _x_y_TextChanged(object sender, TextChangedEventArgs e)
{
TextBox current = (TextBox)sender;
string currentname = current.Name;
string rowstring = currentname.Substring(1, 1);
string columnstring = currentname.Substring(3, 1);
int row = Convert.ToInt32(rowstring);
int column = Convert.ToInt32(columnstring);
// I've then detected the name of the textbox which has called it...
So this information can be used to dynamically store information from a TextBox in a corresponding array index - or whatever you want to do with it...
My question however, is:
How can I create a method which uses index locations in my array, to call the relevant TextBox and update its text?
Use FindName(string) to find the text box by name as follows (where container is a control that contains all of the text boxes):
private void UpdateTextBox(int row, int column, string text)
{
TextBox textBox = container.FindName("_" + row + "_" + column) as TextBox;
if(textbox != null)
{
textbox.Text = text;
}
}
There are two ways you might go:
If you have a lot of data to manage, or if you can't predict the length of the array, it would be better to bind to a collection instead of manually poking data into and out of an array. If you create a class derived from ObservableCollection instead of using an array the data <> ui relationship is pretty trivial.
if you really need to do this manually, maybe it would be better to stick the index into the 'tag' field of your text boxes. You could (a) see it clearly in your xaml, (b) parse it easily and (c) if you used a variation on the formula here:
Find all controls in WPF Window by type
you could iterate over the textboxes in window and find the right one by looking at its tag index:
foreach (TextBox t in FindVisualChildren<TextBox>(this))
{
if ((int) t.Tag) == my_index )
{
t.Text = "my_text_goes_here";
}
}
I would go in the direction of the answer I gave on this question:
form anchor/dock
In short, I would create a class that holds the actual values and then create a collection that holds information classes.
Then I would not use the event "TextChanged" on the TextBoxes, rather "sniff" for changes on the Dependency Property used to hold the text. This can easily be done in the Dependency Property.
Last, I would use an ItemsControl or ItemsPresenter to show the controls. Number of controls will follow number of items in the collection.
I suggest using MVVM pattern, data template, and ItemsControl for handling this problem effectively.

vs2010 automation : Get the text value of a EnvDTE.CodeElement

So I'm playing around with EnvDTE, and the EnvDTE.CodeModel API, And I was wondering if there was a way to get the text value represented by a CodeElement.
Let's say I have an CodeAttribute, is there some way to get a string of what the CodeAttribute represents, i.e.[MyAttribute(value="myvalue")].
I know it's possible to reconstruct the code using the various properties of the CodeElement, at least in some scenarios, but for some things it seems it would be easier to just get the text.
Thanks!
The CodeElement interface has the properties StartPoint and EndPoint which represent the start and end of the element within the buffer. These contain the Line Number / Column which can be passed to methods like IVsTextLines.GetLineText and give you back the value you're looking for.
To get the IVsTextLines for a given CodeElement you can do the following
CodeElement ce = ...;
TextDocument td = ce.StartPoint.Parent;
IVsTextLines lines = td as IVsTextLines;
void WriteMapping(CodeProperty codeProperty)
{
WriteLine("");
WriteLine("///CodeProperty");
WriteLine("///<summary>");
WriteLine("///"+codeProperty.FullName);
WriteLine("///</summary>");
if(codeProperty.Getter==null && codeProperty.Setter==null)
return;
if(codeProperty.Attributes!=null){
foreach(CodeAttribute a in codeProperty.Attributes)
{
Write("["+a.FullName);
if(a.Children!=null && a.Children.Count>0)
{
var start=a.Children.Cast<CodeElement>().First().GetStartPoint();
var finish= a.GetEndPoint();
string allArguments=start.CreateEditPoint().GetText(finish);
Write("("+allArguments);
}
WriteLine("]");
}
}
Write("public "+GetFullName(codeProperty.Type) +" "+codeProperty.Prototype);
Write(" {");
//if(codeProperty.Getter!=null && codeProperty.Getter.Access!=vsCMAccess.vsCMAccessPrivate)
Write("get;");
//if(codeProperty.Setter!=null)
Write("set;");
WriteLine("}");
}
In addition to the answer by #JaredPar, an alternative approach would be:
public string GetText(CodeAttribute attribute)
{
return attribute.StartPoint.CreateEditPoint().GetText(attribute.EndPoint);
}
That's it!! (Thanks #JaredPar for the pointers)
Source: http://msdn.microsoft.com/en-us/library/envdte.editpoint.gettext.aspx

Categories

Resources