roglemorph
roglemorph

Reputation: 23

How can I get properties of a WPF element I have created

I am currently making a tool that allows the user to make multiple types of searches quickly by checking what sites they want to search in a listbox.

I am running into an issue related to adding custom to sites to search -- I would like to have a button that allows the user to add another checkbox website to the ListBox that contains them all. That part is not so difficult, but once I have created the checkbox I need to get the URL of the site the user entered, and I cannot figure out how to reference any aspect of the newly created checkbox.

Currently whenever the user clicks the search button, the programs checks what sites are enabled and then runs whatever code is relevant to the searches that are selected. For the default custom search, it gets the content of the textbox and uses site:textbox.text (in google) to find results from that website

I need to be able to reference the text of a new custom site checkbox and then create a URL out of it. You can see here that I have created a string array which contains the links for other website selections. I attempted to fix this problem by adding sites.Append(newcustomsitetext.text) in the function that creates the new button (button_click_1), but the user has not actually entered a custom website at that point and that string would be more useful outside of the array because I already have the part of the link I need in the array (google.com/searchq=etc).

I also tried to create a method that would append the selection (updateSitesArray) but again I cannot refence the properties of the Textbox outside of the function that created it.

I am very inexperienced with C# and WPF so I'm sure I am doing something (probably many things) wrong here, so I apologize if this is a silly question.

Here is a screenshot of the application C# script (browserDiplay.DetermineURL converts string to URI for webbrowser)

namespace HomeWork_Helper
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    /// 
    
    public partial class MainWindow : Window
    {
        public int selection = 0;
        public ListBox list;
        string[] sites = { "https://www.google.com/search?q=","https://api.wolframalpha.com/v1/simple?i=","http://www.google.com/search?q=site:wikipedia.org","http://www.google.com/search?q=site:"};
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e) //main search button click
        {
            for (int i = 0; i < SitesList.Items.Count; i++)
            {
                string text = SitesList.Items[i].ToString();
                if (text.Contains("IsChecked:True"))
                {
                    if (SitesList.Items[i].Equals(WolframCheck))
                    {
                        BrowserDisplay browserDisplay = new BrowserDisplay(); //create a new browser
                        browserDisplay.Show(); //Show it
                        browserDisplay.determineURL(sites[i] + searchBox.Text + "&appid=2GA4A5-YL7HY9KR42");
                        Console.WriteLine(text);
                        Console.WriteLine((sites[i] + searchBox.Text + "&appid=2GA4A5-YL7HY9KR42"));
                    }
                    else if (SitesList.Items[i].Equals(CustomSiteCheck))
                    {
                        BrowserDisplay browserDisplay = new BrowserDisplay(); //create a new browser
                        browserDisplay.Show(); //Show it
                        browserDisplay.determineURL(sites[i] + CustomSiteEntry.Text + " " + searchBox.Text);
                        Console.WriteLine(text);
                    }
                    else if (i > 4)    //if i > 4 listbox item is new 
                    {
                        BrowserDisplay browserDisplay = new BrowserDisplay(); //create a new browser
                        browserDisplay.Show(); //Show it
                        browserDisplay.determineURL(sites[i] + ***How can I reference a new element here?*** + " " + searchBox.Text);
                        Console.WriteLine(text);
                    }
                    else
                    {
                        BrowserDisplay browserDisplay = new BrowserDisplay(); //create a new browser
                        browserDisplay.Show(); //Show it
                        browserDisplay.determineURL(sites[i] + " " + searchBox.Text);
                        Console.WriteLine(text);
                    }
                }
            }
            
        }
        
        public void Button_Click_1(object sender, RoutedEventArgs e) //creates a new buton
        {
            
            CheckBox newcustomcheck = new CheckBox();
            TextBox customsitetext = new TextBox();
            customsitetext.Width = 215;
            customsitetext.Text = "Enter a custom site here";
            newcustomcheck.Content = customsitetext;
            //newcustomcheck.Checked += updateSitesArray;
            SitesList.Items.Add(newcustomcheck);
            sites.Append(customsitetext.Text);
                
        }
        private void updateSitesArray(TextBox sender, EventArgs e)
        {
            
        }
    }
}

XAML code :

<Window x:Class="HomeWork_Helper.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HomeWork_Helper"
        mc:Ignorable="d"
        Title="MainWindow" Background="DarkGray" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="20"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="20" />
            <RowDefinition Height="Auto" MinHeight="60"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="20" />
        </Grid.RowDefinitions>

        <TextBox Name="searchBox" HorizontalAlignment="Left" Height="60" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="501" Grid.Column="2" Grid.ColumnSpan="2" Grid.Row="1"/>
        <Button Name ="searchButton" Content="Search" HorizontalAlignment="Left" Height="60" VerticalAlignment="Top" Width="251" Grid.Column="1" Grid.Row="1" Click="Button_Click"/>

        <ListBox Name="SitesList" Grid.Column="1" HorizontalAlignment="Left" Height="160" Grid.Row="2" VerticalAlignment="Top" Width="251">
            <CheckBox Name="GoogleCheck" Content="Google Search" IsChecked="false"/>
            <CheckBox Name="WolframCheck" Content="Wolfram Search (slow)" IsChecked="false"/>
            <CheckBox Name="WikipediaCheck" Content="Wikipedia Search" IsChecked="false"/>
            <CheckBox Name="CustomSiteCheck" >
                <TextBox Name="CustomSiteEntry" Text="Enter a cutstom site here." Width="215"/>
            </CheckBox>
        </ListBox>
        <Button Content="Add another custom site" Grid.Column="1" HorizontalAlignment="Left" Height="40" Grid.Row="3" VerticalAlignment="Top" Width="250" Click="Button_Click_1"/>





    </Grid>
</Window>

Upvotes: 1

Views: 683

Answers (1)

BionicCode
BionicCode

Reputation: 28968

Never create the items directly. Always use a data model and let the framework create the items automatically/dynamically by defining a DataTemplate. This will make data handling much easier, reduce related code and improves readability significantly.
Always operate on the data model. Do not operate on the view.

Another important design principle is to avoid type switches or chained if-statements that check for a data type to execute related action. Such code can't scale: adding new data types would break your code. You would also require to add new if-statements. Code will become ugly and hard to maintain.

In your case simply encapsulate the individual information (or logic) and move it into a dedicated class e.g. Site. Each Site knows the correct URL or how to assemble it. The caller does not have have to care about this details. Simply get the assembled URL and launch the browser to execute the search. This also enables clean polymorphism and removes duplicate code: simply iterate over the Site items and start a search based on each Site data using a single generic search implementation.

The following example is a raw template how get started. Since you are developing using WPF, I recommend to learn and use MVVM as soon as possible. It will make developing much easier.

Please read:
INotifyPropertyChanged (to allow data binding), Data binding overview, Data templating overview (and Dependency properties overview) to fully understand the example.

Site.cs

public class Site : INotifyPropertyChanged
{
  private string title;
  public string Title
  {
    get => this.title;
    set
    {
      this.title = value;
      OnPropertyChanged();
    }
  }

  private string url;
  public string Url
  {
    get => this.url;
    set
    {
      this.url = value;
      OnPropertyChanged();
    }
  }

  private bool isEnabled;
  public bool IsEnabled
  {
    get => this.isEnabled;
    set
    {
      this.isEnabled = value;
      OnPropertyChanged();
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
  protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

MainWindow.xaml.cs

public partial class MainWindow : Window
{
  // This property should really be a DependencyProperty
  public ObservableCollection<Site> SearchSites { get; }

  public MainWindow()
  {
    InitializeComponent();
    this.DataContext = this;

    this.SearchSites = new ObservableCollection<Site>
    {
      new Site() { Title = "DuckDuckGo", Url = "https://duckduckgo.com/?q=" }
    };
  }

  private void StartSearch_OnClick(object sender, RoutedEventArgs e)
  {
    IEnumerable<Site> enabledSearchSites = this.SearchSites.Where(site => site.IsEnabled);
    foreach (Site searchSite in enabledSearchSites)
    {
      var browserDisplay = new BrowserDisplay(); //create a new browser
      browserDisplay.Show(); //Show it
      browserDisplay.determineURL(searchSite.Url + this.SearchBox.Text);
    }
  }

  public void AddNewSearchSite_OnClick(object sender, RoutedEventArgs e)
  {
    var newSearchSite = new Site 
    { 
      Title = "Enter a custom site name here", 
      Url = "Enter a custom site URL here" 
    };

    // Since SearchSites is an ObservableCollection, adding/removing an item 
    // will instantly update the view (binding target)
    this.SearchSites.Add(newSearchSite);
  }
}

MainWindow.xaml

<ListBox ItemsSource="{Binding SearchSites}">
  <ListBox.ItemTemplate>

    <!-- Describe how each item should look like 
         and wire it to the data model using data binding -->
    <DataTemplate DataType="{x:Type local:Site}">
      <CheckBox IsChecked="{Binding IsEnabled}">
        <CheckBox.Content>
          <StackPanel Orientation="Horizontal">
            <TextBox Text="{Binding Title}" />
            <TextBox Text="{Binding Url}" />
          </StackPanel>
        </CheckBox.Content>
      </CheckBox>
    </DataTemplate>
  </ListBox.ItemTemplate>
</ListBox>

Upvotes: 2

Related Questions