Click here to Skip to main content
15,886,199 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a WPF desktop application. This applications have different tabs with different control. My requirement is below:-

1. I need to implement ctrl+F search box which can search labels of the control and highlight them.
e.g.
Security Name <drop down="" control="">

so here if I do search on Security Name, the control should get focus and my scroll positions need to be adjusted.

How I can achieve it? I can think of different designs but want to know best design form WPF front as I am new to WPF.

What I have tried:

My Thoughts:-
1. Check if i can get control from virtual tree
2. Set focus and move scroll position to control with some box highlighting.

I have not yet coded anything but this is what i can think. Not sure if doable.
Posted
Updated 31-Jul-21 7:22am

It is eminently doable but you will only realise that by starting to code it.

To answer your point 1 - try using the VisualTreeHelper Class[^] - the example in the documentation actually enumerates all of the controls.

First link returned by a google search : Find controls by name in WPF with C# - C# HelperC# Helper[^] - just adapt it to your needs.

Re your point 2... First link returned by a google search : How to: Set Focus in a TextBox Control - WPF .NET Framework | Microsoft Docs[^]

If you get stuck once you have written some code then by all means come back with a specific question, but check the posting guidelines - we expect you to have at least done some basic research for yourself.
Quote:
I can think of different designs but want to know best design form WPF front as I am new to WPF.
"best" is subjective - what is best for me might not be best for you. So try those different designs. Being new to anything just means you need to do some study and some research. That never goes away by the way - even experienced programmers continue to study and research. We can't do that for you.
 
Share this answer
 
v2
There is no standard way in WPF that provides this specific functionality out-of-the-box. However, there are several ways this can be done.

One way I can think of, which should be relatively easy to implement, is to use attached properties. Using attached properties, you would have to opt in on each label you wish to find text.

Because each control may use different properties to set their text, you may prefer to have a way of defining what text is to be found on the control. For example, TextBlock objects use Text property, while Button objects use Content property, which may or may not be a string. To make this work, you can create an attached property on the Window (or some other class) that accepts a string and set that attached property on each control with a binding to the actual text property like:
XML
<TextBlock Text="Some text" MyWindow.MyAttachedProperty="{Binding Text, RelativeSource={RelativeSource Mode=Self}}"/>


Although the solution I mention above is versatile, it may not be the most practical and requires you writing a binding for each findable control. If you only wish to find specific control types, like Label, TextBlock and Button, for instance, you would simply create an inheritable boolean attached property that accepts a UIElement. Then you would use like this:
XML
<TextBlock Text="Some text" MyWindow.IsFindable="True"/>


Or, because MyWindow.IsFindable property would be inheritable, you could set it only once on the parent container:
XML
<Grid MyWindow.IsFindable="True">
    <TextBlock Text="Some text"/>
    <Button Content="Button text"/>
</Grid>


Now, how would you implement such attached property? Well, you could set up a static HashSet<UIElement> instance that stores the findable UIElement instances, like the following:

C#
// This code must be inside a DependencyObject, like MyWindow.

private static readonly Lazy<HashSet<UIElement>> _findableElements = new();
private static HashSet<UIElement> FindableElements => _findableElements.Value;

public static readonly DependencyProperty IsFindableProperty 
    = DependencyProperty.RegisterAttached("IsFindable", typeof(bool), typeof(MyWindow), 
      new FrameworkPropertyMetadata(false, 
          FrameworkPropertyMetadataOptions.Inherits,
          new PropertyChangedCallback(IsFindablePropertyChanged)));

private static void IsFindablePropertyChanged(
    DependencyObject dObj, DependencyPropertyChangedEventArgs e)
{
    UIElement element = (UIElement)dObj;
    bool value = (bool)e.NewValue;
    // This will add or remove the element to the FindableElements set, depending on the IsFindable property value of the element.
    if (value)
    {
        FindableElements.Add(element);
    }
    else
    {
        FindableElements.Remove(element);
    }
}

public static void SetIsFindable(UIElement element, bool isFindable)
{
    element?.SetValue(IsFindableProperty, isFindable) ?? throw new ArgumentNullException(nameof(element));
}

public static bool GetIsFindable(UIElement element)
{
    (bool)(element?.GetValue(IsFindableProperty) ?? throw new ArgumentNullException(nameof(element)));
}

// You would call this method to find elements using your own matching logic. 
// Alternatively, you could even accept a Func<UIElement, string> parameter that
// would be called to extract the text from the UIElements using your own 
// custom logic. Also, because this returns an enumerable, the search is only 
// performed per each match iteration (lazy search).
public static IEnumerable<UIElement> FindInElements(Predicate<string> predicate)
{
    if (predicate is null)
        throw new ArgumentNullException(nameof(predicate));

    foreach (var element in FindableElements)
    {
        // Extract the text from the element. We could be using a custom function provided as a parameter, as well.
        string elementText = GetTextFromElement(element);
        // Here, you check if the element's text matches the find query criteria using the logic you provided on the predicate.
        if (!string.IsNullOrEmpty(elementText) && predicate.Invoke(elementText))
        {
            yield return element;
        }
    }
}

private static string GetTextFromElement(UIElement element)
{
    // Here, you get the text from the UIElement, depending on the type.
    // You would add the logic for each type as you wish. This is just an example.
    if (element is TextBlock textBlock)
        return textBlock.Text;
    else if (element is ContentElement contentElement)
        return contentElement.Content as string;
    else
        return null;
}


The code above would be inside your window (the example assumes MyWindow), but could be inside any other container. Because the attached property is inheritable, you would set IsFindable="True" only on the window and still opting out any element inside by setting IsFindable="False" on that specific element.

To find elements, you would then use code like this:
C#
// Find elements that contain the specified query.
string query = "Some text to find";
foreach (var element in FindInElements(text => text.Contains(query)))
{
    // Do something with the found element.
}


There are several other ways of achieving similar results, and this is just one of them. You can traverse the logical tree, you can use attached properties differently, etc.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900