Web- und Software Development

Text innerhalb einer DataGrid-Zelle (cell) mit Fettschrift (bold) formatieren

Written By: Mario Priebe - Mai• 19•11

Auf Codekicker wurde eine Frage gestellt, die mich doch etwas mehr an Zeit gekostet hat, als ich gedacht habe. Bei der Fragestellung ging es darum, einen gebundenen Text innerhalb der Zelle zu formatieren. Ein String in der Zelle sollte so formatiert werden, dass bei der Übergabe von Keywords, diese Fett dargestellt werden sollen. (siehe Bild am Ende des Artikels)

Mein erster Gedanke war, dass ich in einem Converter die Parameter innerhalb des Strings suche, diese dann mit der Run-Klasse “ummantle” und zurückgebe. Doch leider war das nicht ganz so trivial, wie ich zuerst dachte.

Nun denn, ich hab’s doch hinbekommen, wie, dass möchte ich im folgenden Artikel zeigen.

Mein Converter ist zur ersten Hälfte eigentlich so geworden, wie ich es ursprünglich vorhatte. Anstelle aber einen String zurück zugeben, musste ich den formatierten Text als XAML in einen StringBuilder einlesen und diesen über den XmlReader zu einem TextBlock-Control erstellen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    string s = (string)value;
 
    //Suchwörter ermitteln
    List<string> keywords = ((string)parameter).Split(new char[] { ' ' }).ToList();
 
    //Suchwörter replacen
    keywords.ForEach(k => s = s.Replace(k, String.Format("<Bold>{0}</Bold>", k)));
 
    //TextBlock bauen
    StringReader sr = new StringReader(string.Format(@"<TextBlock xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'>{0}</TextBlock>", s));
 
    XmlReader reader = XmlReader.Create(sr);
    return ((TextBlock)XamlReader.Load(reader)).Inlines.ToList<Inline>();
}
 
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
    throw new NotImplementedException();
}

Den TextBlock gebe ich nun zurück und stelle diesen in einem ItemsControl dar. Dachte ich…

Nach mehreren Hin & Her hat es nicht so richtig hingehauen, entweder zeigte der mir den Text ohne WordWrapping an, oder, ach was weiß ich, was ich noch für merkwürdige Ergebnisse hatte.

Nach etwas Recherche bin ich dann darauf gekommen, den TextBlock abzuleiten und die erweiterte Klasse um ein DependenyProperty zu erweitern. Und tada, gibts schon : )

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
public class BindableTextBlock : TextBlock
{
    public static readonly DependencyProperty InlineCollectionProperty = DependencyProperty.Register("InlineCollection",
        typeof(List<Inline>),
        typeof(BindableTextBlock),
        new UIPropertyMetadata(OnInlineCollectionChanged));
 
    private static void OnInlineCollectionChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        BindableTextBlock instance = sender as BindableTextBlock;
 
        if (instance != null)
        {
            List<Inline> newText = e.NewValue as List<Inline>;
            if (newText != null)
            {
                instance.Inlines.Clear();
                instance.Inlines.AddRange(newText.ToList());
            }
        }
    }
 
    public List<Inline> InlineCollection
    {
        get { return (List<Inline>)GetValue(InlineCollectionProperty); }
        set { SetValue(InlineCollectionProperty, value); }
    }
}

Perfekt, genau das was ich brauche.

Im Xaml das Ganze nur noch in die richtige Reihenfolge gebracht:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<Grid>
    <Grid.Resources>
        <local:KeywordConverter x:Key="KeywordConverter" />
    </Grid.Resources>
 
 
    <DataGrid Name="dataGrid1" AutoGenerateColumns="False" ItemsSource="{Binding Books}">
        <DataGrid.Columns>
            <DataGridTextColumn Header="Name" Binding="{Binding Title}" />
            <DataGridTemplateColumn Header="Description" Width="*">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <Grid>
                            <local:BindableTextBlock TextWrapping="WrapWithOverflow" InlineCollection="{Binding Description, Converter={StaticResource KeywordConverter}, 
                                ConverterParameter='Spur Emma Botschaft Schwur' }"/>
                        </Grid>
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

sieht das Ganze wie folgt aus:

Die ConverterParameter könnten nun auch an eine Textbox o.ä. gebunden werden.
Wer das Beispiel “von Nahen” sehen möchte, kann dieses auch gerne herunterladen.
Ich würde mich freuen, wenn einer da draußen ist, der vielleicht noch eine andere Idee hat, wenn ja, dann immer her damit!

Na, dann. Viel Spaß beim entwickeln : )

Prüfen ob ein String eine Guid ist

Written By: Mario Priebe - Mai• 05•11

Es gibt jede Menge Ansätze, um zu prüfen ob ein String eine Guid ist. Zum einen ist es möglich, den String mit RegEx zu prüfen:

1
2
3
4
5
6
using System.Text.RegularExpressions;
...
private bool IsStringValidGuid(string s)
{
    return string.IsNullOrEmpty(s) ? false : new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$").IsMatch(s);
}

Als ExtensionMethod könnte das wie folgt aussehen:

1
2
3
4
5
6
using System.Text.RegularExpressions;
...
public static bool IsStringValidGuid(this string s)
{
    return string.IsNullOrEmpty(s) ? false : new Regex(@"^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$").IsMatch(s);
}

Zum anderen kann man sich jeden Character in dem String anschauen und prüfen ob dieser im Hexadezimalsystem vorkommt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary>
/// Prueft ob der uebergebene String eine Guid ist.
/// </summary>
/// <param name="s">string</param>
/// <returns>bool</returns>
public bool IsStringValidGuid(string s)
{
    int hexchars = 0;
    foreach (char c in s)
    {
        if (IsValidHexChar(c))
            hexchars++;
    }
    return hexchars == 32;
}
private bool IsValidHexChar(char hexChar)
{
    return (hexChar >= 0x30 && hexChar <= 0x39) //IsValidHexDigit
        || (hexChar >= 0x61 && hexChar <= 0x66) //IsValidLowerHexLetter
        || (hexChar >= 0x41 && hexChar <= 0x46); //IsValidUpperHexLetter
}

Als ExtensionMethod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary>
/// Prueft ob der uebergebene String eine Guid ist.
/// </summary>
/// <param name="s">string</param>
/// <returns>bool</returns>
public static bool IsStringValidGuid(this string s)
{
    int hexchars = 0;
    foreach (char c in s)
    {
        if (IsValidHexChar(c))
            hexchars++;
    }
    return hexchars == 32;
}
private bool IsValidHexChar(char hexChar)
{
    return (hexChar >= 0x30 && hexChar <= 0x39) //IsValidHexDigit
        || (hexChar >= 0x61 && hexChar <= 0x66) //IsValidLowerHexLetter
        || (hexChar >= 0x41 && hexChar <= 0x46); //IsValidUpperHexLetter
}

Die RegEx-Version finde ich eleganter (da Einzeiler), jedoch die Zeichen einzeln zu prüfen, finde ich für das Verständnis besser.

Wie auch immer, funktionieren werden sicher noch mehr Wege…
In diesem Sinne, viel Spaß beim entwickeln : )

Wasserzeichen-TextBox Windows Phone 7

Written By: Mario Priebe - Apr• 24•11

Man kennt diese aus dem Web, Textboxen die mit einem leicht grauen InfoText schon vorbefüllt sind. Klickt der Anwender in diese Box, so verschwindet der Text und der Anwender kann seinen Text eingeben. Gibt dieser nichts ein, so wird der “Watermark” Text wieder angezeigt.

Dieses Szenario habe ich mal für eine Windows Phone 7 Textbox programmiert. Ich verwende zur Unterscheidung, die Farbe die man in einer ReadOnly Textbox bekommt.

Im weiteren Handling mit der TextBox, muss man natürlich den Inhalt der WatermarkText-Property mit der TextBoxText Property prüfen.

1
2
3
xmlns:controls="clr-namespace:WatermarkTextBox"
...
<controls:WatermarkTextBox WatermarkText="type your email here" />
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
public class WatermarkTextBox : TextBox
{
    public string WatermarkText
    {
        get { return (String)this.GetValue(WatermarkTextProperty); }
        set { this.SetValue(WatermarkTextProperty, value); }
    }
    public static readonly DependencyProperty WatermarkTextProperty =
        DependencyProperty.Register("WatermarkText", typeof(String), typeof(WatermarkTextBox), new PropertyMetadata(String.Empty));
 
    public SolidColorBrush WatermarkForeGroundColor = Application.Current.Resources["PhoneTextBoxReadOnlyBrush"] as SolidColorBrush;
 
 
    public WatermarkTextBox()
    {
        Loaded += new RoutedEventHandler(WatermarkTextBox_Loaded);
    }
 
    void WatermarkTextBox_Loaded(object sender, RoutedEventArgs e)
    {
        this.Text = !String.IsNullOrEmpty(WatermarkText) ? WatermarkText : String.Empty;
        this.Foreground = WatermarkForeGroundColor;
    }
 
    protected override void OnGotFocus(RoutedEventArgs e)
    {
        base.OnGotFocus(e);
 
        if (WatermarkText == this.Text)
        {
            this.Text = String.Empty;
            this.Foreground = Application.Current.Resources["PhoneTextBoxForegroundBrush"] as SolidColorBrush;
        }
    }
 
    protected override void OnLostFocus(RoutedEventArgs e)
    {
        base.OnLostFocus(e);
 
        if (String.IsNullOrEmpty(this.Text))
        {
            this.Text = !String.IsNullOrEmpty(WatermarkText) ? WatermarkText : String.Empty;
            this.Foreground = WatermarkForeGroundColor;
        }
    }
}

Viel Spaß beim entwickeln : )

Theme Resources im Windows Phone 7

Written By: Mario Priebe - Apr• 24•11

Die Windows Phone 7 internen Farbschemen können man über die Theme Resources ermittelt und in eigenen Controls verwendet werden. Im MSDN steht einem eine Liste der verfügbaren Ressourcen bereit.

Zugewiesen werden diese wie folgt:

1
2
public SolidColorBrush MyOwnForeGroundColor = Application.Current.Resources["PhoneTextBoxReadOnlyBrush"] as SolidColorBrush;
this.Foreground = MyOwnForeGroundColor;