Stunna
Stunna

Reputation: 413

How to bind textblock.foreground to a variable? (WPF C#)

So I am hoping to alter my program such that I can run a function to check and see if the foreground color should be black or silver. I am hoping to gray out fields that are not "accessible".

My form currently looks like: enter image description here

I was hoping to "gray out" the "No maintenance required" fields. But I am having problems with trying to define a binding element to the font foreground in my data template.

I've tried everything from trying to define an IValueConverter class within the main window code behind, to defining a window key resource, but it doesn't appear that I can do that within a data template on the textblock element itself?

Any suggestions/help would be appreciated. Thanks!

XAML:

<Grid Margin="0,0,2,0">

    <ListBox x:Name="allSites_LB" 
             HorizontalAlignment="Left" 
             Height="400" 
             Margin="20,60,0,0" 
             VerticalAlignment="Top" 
             Width="945"
             Loaded="allSites_LB_Loaded" 
             BorderThickness="1" SelectionChanged="allSites_LB_SelectionChanged"
             ScrollViewer.HorizontalScrollBarVisibility="Disabled"
             >
        <ListBox.ItemTemplate >
            <DataTemplate >
                <Border BorderBrush="Black" BorderThickness="0,0,0,1" Margin="-20,1,0,1" Padding="0,5,0,5" >
                    <Grid Margin="75,3" >
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="200" />
                            <ColumnDefinition Width="400" />
                            <ColumnDefinition Width="345" />
                        </Grid.ColumnDefinitions>
                        <TextBlock Text="{Binding SiteNo}" Grid.Column="0" FontSize="16" />
                        <TextBlock Text="{Binding Address}" Grid.Column="1" FontSize="16" Margin="50,1" />
                        <TextBlock Text="{Binding MaintStatus}" Grid.Column="2" FontSize="16" />
                    </Grid>
                </Border>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

    <Button x:Name="viewHistory_BTN" 
            Content="View History" 
            HorizontalAlignment="Left" 
            Height="52" 
            Margin="20,496,0,0" 
            VerticalAlignment="Top" 
            Width="172" FontSize="20"
            />

    <Button x:Name="startMaintenance_BTN" 
            Content="Start Maintenance"
            HorizontalAlignment="Left" 
            Height="52" 
            Margin="793,496,0,0" 
            VerticalAlignment="Top" 
            Width="172" FontSize="20"
            />
    <TextBox x:Name="Site_Address" 
             HorizontalAlignment="Left" 
             Height="21" 
             Margin="51,39,0,0" 
             TextWrapping="Wrap" 
             Text="Site Number" 
             VerticalAlignment="Top" 
             Width="75" 
             BorderBrush="White" 
             IsReadOnly="True" 
             IsEnabled="False"

             />
    <TextBox x:Name="Address_Title" 
             HorizontalAlignment="Left" 
             Height="21" 
             Margin="380,34,0,0" 
             TextWrapping="Wrap" 
             Text="Address" 
             VerticalAlignment="Top" 
             Width="75" 
             BorderBrush="White"
             IsReadOnly="True" 
             IsEnabled="False"

             />
    <TextBox x:Name="maint_Title" 
             HorizontalAlignment="Left" 
             Height="21" 
             Margin="699,34,0,0" 
             TextWrapping="Wrap" 
             Text="Maintenance Record" 
             VerticalAlignment="Top" 
             Width="117" 
             BorderBrush="White" 
             IsReadOnly="True" 
             IsEnabled="False"
             />

</Grid>

C# Code Behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.SqlClient;



namespace SiteMaintenance
{

public partial class MainWindow : Window
{

    /**
     * CLASS VARIABLES
     * */
    private SqlConnection localdbConnection;        // Connection to Site Maintenance DB (LOCAL)
    private System.Data.DataSet allSitesResults;



    // MAIN THREAD
    public MainWindow()
    {
        InitializeComponent();

        // try to open SQL Connection
        try {
            localdbConnection = new SqlConnection(Properties.Settings.Default.localdb);
            localdbConnection.Open();
        } catch(Exception ex) {
           System.Windows.MessageBox.Show("local SQL connection unable to connect");
           return;
        }

        viewHistory_BTN.IsEnabled = false;
        startMaintenance_BTN.IsEnabled = false;
        startMaintenance_BTN.IsDefault = true;
    }

    /**
     * Load dataset into datagrid 
     * LAZY LOADING
     * */
    private void DataGrid_Loaded(object sender, RoutedEventArgs e)
    {
        // init command object
        SqlCommand myCommand = new SqlCommand();
        myCommand.CommandText = "dbo.usp_GetSites";
        myCommand.CommandType = System.Data.CommandType.StoredProcedure;
        myCommand.Connection = localdbConnection;

        // init data adaptor
        SqlDataAdapter sites = new SqlDataAdapter();
        sites.SelectCommand = myCommand;

        //init DataSet
        allSitesResults = new System.Data.DataSet();

        sites.Fill(allSitesResults, "tblSites");

        int tableCount = allSitesResults.Tables.Count;

        System.Data.DataTable test = allSitesResults.Tables[0];

        int rowCount = test.Rows.Count;





    }

    private void sites_DG_CurrentCellChanged(object sender, EventArgs e)
    {
        String siteName = allSitesResults.Tables[0].Rows[0][1].ToString();

    }

    private void allSites_LB_Loaded(object sender, RoutedEventArgs e)
    {
        // init command object
        SqlCommand myCommand = new SqlCommand();
        myCommand.CommandText = "dbo.usp_GetSitesANDCompletedDate";
        myCommand.CommandType = System.Data.CommandType.StoredProcedure;
        myCommand.Connection = localdbConnection;

        // init data adaptor
        SqlDataAdapter sites = new SqlDataAdapter();
        sites.SelectCommand = myCommand;

        //init DataSet
        allSitesResults = new System.Data.DataSet();

        sites.Fill(allSitesResults, "tblSites");

        allSites_LB.ItemsSource = allSitesResults.Tables["tblSites"].DefaultView;

    }


    // do not allow selection of maintenance records that do not exist
    private void allSites_LB_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {

        // grab the index
        int selectedIndex = allSites_LB.SelectedIndex;
        if (selectedIndex == -1) return;                //WITHOUT THIS CHECK, UN-SELECTION WILL CAUSE LOGIC FAILURE

        System.Data.DataRowView tempData = (System.Data.DataRowView)allSites_LB.Items[allSites_LB.SelectedIndex];

        // grab the completed date field
        String completedDate = tempData["CompletedDate"].ToString();
        String siteMaintID = tempData["SiteMaintID"].ToString();

        // remove selected index if completed date and site Maint ID is null
        if (siteMaintID != "" && completedDate == "")
        {
            startMaintenance_BTN.IsEnabled = true;
        }
        else 
        {
            allSites_LB.SelectedIndex = -1;
            startMaintenance_BTN.IsEnabled = false;
        }

    }


    private String maintRequired(object sender, SelectionChangedEventArgs e)
    {
        int selectedIndex = allSites_LB.SelectedIndex;
        if (selectedIndex < 0) return null;

        System.Data.DataRowView tempData = (System.Data.DataRowView)allSites_LB.Items[allSites_LB.SelectedIndex];

        // grab the completed date field
        String completedDate = tempData["CompletedDate"].ToString();
        String siteMaintID = tempData["SiteMaintID"].ToString();

        if (siteMaintID != "" && completedDate == "")
        {
            return "Maintenance Required";
        }
        else
        {
            return "No Maintenance";
        }
    }

}

}

Upvotes: 3

Views: 8897

Answers (3)

Stunna
Stunna

Reputation: 413

Thanks for the feedback! I went with BTownTKD's suggestion on implementing an IValueConverter although with some alterations in my code. I discovered I needed to define "local" scope in my window properties in XAML.

Also, I was discovering that the binding wasn't actually changing the text color. After stepping through the code and seeing that the method was being properly invoked, I then hardcoded the results returned into the XAML to make sure they were working (foreground="black" or foreground="#FF00000"). I noticed when stepping through the code that the return object was a "color" object in the original binding, and by me hard-coding the colors into the XAML, they were actually strings. So I altered the code slightly to add in a .ToString() to the object I was returning and VOILA it worked! Thanks again for the help!

FYI here's the updated code bits:

XAML:

   <Window x:Class="SiteMaintenance.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:SiteMaintenance"
        Title="MainWindow" 
        Height="600" 
        Width="1000">
    <Window.Resources>
        <local:MaintenenceColorConverter x:Key="MyColorConverter" NormalColor="Black" NoMaintenanceRequiredColor="Gray" />
    </Window.Resources>

    <Grid Margin="0,0,2,0">

        <ListBox x:Name="allSites_LB" 
                 HorizontalAlignment="Left" 
                 Height="400" 
                 Margin="20,60,0,0" 
                 VerticalAlignment="Top" 
                 Width="945"
                 Loaded="allSites_LB_Loaded" 
                 BorderThickness="1" SelectionChanged="allSites_LB_SelectionChanged"
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled"
                 >
            <ListBox.ItemTemplate >
                <DataTemplate >
                    <Border BorderBrush="Black" BorderThickness="0,0,0,1" Margin="-20,1,0,1" Padding="0,5,0,5" >
                        <Grid Margin="75,3" >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="200" />
                                <ColumnDefinition Width="400" />
                                <ColumnDefinition Width="345" />
                            </Grid.ColumnDefinitions>
                            <TextBlock Text="{Binding SiteNo}" Grid.Column="0" FontSize="16" Foreground="{Binding MaintStatus, Converter={StaticResource MyColorConverter}}"  />
                            <TextBlock Text="{Binding Address}" Grid.Column="1" FontSize="16" Margin="50,1" Foreground="{Binding MaintStatus, Converter={StaticResource MyColorConverter}}"  />
                            <TextBlock Text="{Binding MaintStatus}" Grid.Column="2" FontSize="16" Foreground="{Binding MaintStatus, Converter={StaticResource MyColorConverter}}" />
                        </Grid>
                    </Border>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

        <Button x:Name="viewHistory_BTN" 
                Content="View History" 
                HorizontalAlignment="Left" 
                Height="52" 
                Margin="20,496,0,0" 
                VerticalAlignment="Top" 
                Width="172" FontSize="20"
                />

        <Button x:Name="startMaintenance_BTN" 
                Content="Start Maintenance"
                HorizontalAlignment="Left" 
                Height="52" 
                Margin="793,496,0,0" 
                VerticalAlignment="Top" 
                Width="172" FontSize="20"
                />
        <TextBox x:Name="Site_Address" 
                 HorizontalAlignment="Left" 
                 Height="21" 
                 Margin="51,39,0,0" 
                 TextWrapping="Wrap" 
                 Text="Site Number" 
                 VerticalAlignment="Top" 
                 Width="75" 
                 BorderBrush="White" 
                 IsReadOnly="True" 
                 IsEnabled="False"

                 />
        <TextBox x:Name="Address_Title" 
                 HorizontalAlignment="Left" 
                 Height="21" 
                 Margin="380,34,0,0" 
                 TextWrapping="Wrap" 
                 Text="Address" 
                 VerticalAlignment="Top" 
                 Width="75" 
                 BorderBrush="White"
                 IsReadOnly="True" 
                 IsEnabled="False"

                 />
        <TextBox x:Name="maint_Title" 
                 HorizontalAlignment="Left" 
                 Height="21" 
                 Margin="699,34,0,0" 
                 TextWrapping="Wrap" 
                 Text="Maintenance Record" 
                 VerticalAlignment="Top" 
                 Width="117" 
                 BorderBrush="White" 
                 IsReadOnly="True" 
                 IsEnabled="False"
                 />

    </Grid>
</Window>

C# Code Behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Data.SqlClient;



namespace SiteMaintenance
{

public partial class MainWindow : Window
{

    /**
     * CLASS VARIABLES
     * */
    private SqlConnection localdbConnection;        // Connection to Site Maintenance DB (LOCAL)
    private System.Data.DataSet allSitesResults;



    // MAIN THREAD
    public MainWindow()
    {
        InitializeComponent();

        // try to open SQL Connection
        try {
            localdbConnection = new SqlConnection(Properties.Settings.Default.localdb);
            localdbConnection.Open();
        } catch(Exception ex) {
           System.Windows.MessageBox.Show("local SQL connection unable to connect");
           return;
        }

        viewHistory_BTN.IsEnabled = false;
        startMaintenance_BTN.IsEnabled = false;
        startMaintenance_BTN.IsDefault = true;
    }

    /**
     * Load dataset into datagrid 
     * LAZY LOADING
     * */
    private void DataGrid_Loaded(object sender, RoutedEventArgs e)
    {
        // init command object
        SqlCommand myCommand = new SqlCommand();
        myCommand.CommandText = "dbo.usp_GetSites";
        myCommand.CommandType = System.Data.CommandType.StoredProcedure;
        myCommand.Connection = localdbConnection;

        // init data adaptor
        SqlDataAdapter sites = new SqlDataAdapter();
        sites.SelectCommand = myCommand;

        //init DataSet
        allSitesResults = new System.Data.DataSet();

        sites.Fill(allSitesResults, "tblSites");

        int tableCount = allSitesResults.Tables.Count;

        System.Data.DataTable test = allSitesResults.Tables[0];

        int rowCount = test.Rows.Count;

    }


    private void sites_DG_CurrentCellChanged(object sender, EventArgs e)
    {
        String siteName = allSitesResults.Tables[0].Rows[0][1].ToString();

    }

    private void allSites_LB_Loaded(object sender, RoutedEventArgs e)
    {
        // init command object
        SqlCommand myCommand = new SqlCommand();
        myCommand.CommandText = "dbo.usp_GetSitesANDCompletedDate";
        myCommand.CommandType = System.Data.CommandType.StoredProcedure;
        myCommand.Connection = localdbConnection;

        // init data adaptor
        SqlDataAdapter sites = new SqlDataAdapter();
        sites.SelectCommand = myCommand;

        //init DataSet
        allSitesResults = new System.Data.DataSet();

        sites.Fill(allSitesResults, "tblSites");

        allSites_LB.ItemsSource = allSitesResults.Tables["tblSites"].DefaultView;

    }


    // do not allow selection of maintenance records that do not exist
    private void allSites_LB_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {

        // grab the index
        int selectedIndex = allSites_LB.SelectedIndex;
        if (selectedIndex == -1) return;                //WITHOUT THIS CHECK, UN-SELECTION WILL CAUSE LOGIC FAILURE

        System.Data.DataRowView tempData = (System.Data.DataRowView)allSites_LB.Items[allSites_LB.SelectedIndex];

        // grab the completed date field
        String completedDate = tempData["CompletedDate"].ToString();
        String siteMaintID = tempData["SiteMaintID"].ToString();

        // remove selected index if completed date and site Maint ID is null
        if (siteMaintID != "" && completedDate == "")
        {
            startMaintenance_BTN.IsEnabled = true;
        }
        else 
        {
            allSites_LB.SelectedIndex = -1;
            startMaintenance_BTN.IsEnabled = false;
        }

    }


    private String maintRequired(object sender, SelectionChangedEventArgs e)
    {
        int selectedIndex = allSites_LB.SelectedIndex;
        if (selectedIndex < 0) return null;

        System.Data.DataRowView tempData = (System.Data.DataRowView)allSites_LB.Items[allSites_LB.SelectedIndex];

        // grab the completed date field
        String completedDate = tempData["CompletedDate"].ToString();
        String siteMaintID = tempData["SiteMaintID"].ToString();

        if (siteMaintID != "" && completedDate == "")
        {
            return "Maintenance Required";
        }
        else
        {
            return "No Maintenance";
        }
    }

}

public class MaintenenceColorConverter : IValueConverter
{

    public Color NormalColor { get; set; }
    public Color NoMaintenanceRequiredColor { get; set; }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {

        if (value.ToString() == "No Maintenance Required") return NoMaintenanceRequiredColor.ToString();

        return NormalColor.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }

}

}

I'll be cleaning up my code later with BTown's optimization, but at least its working!

Upvotes: 1

Breealzibub
Breealzibub

Reputation: 8095

There are generally two good approaches for you to choose from, when binding the Foreground color to a piece of data. Depending on who you ask, different people will have different preferences. So... here's both!

First Method: Style with Triggers

This method basically identifies 'special' behavior when a certain set of conditions are met. In this case, we're changing the foreground color to Gray, when the status == "No Maintenance Required"

<Style TargetType="TextBlock">
    <Setter Property="Foreground" Value="Black" /> <!-- default value -->
    <Style.Triggers>
        <DataTrigger Binding="{Binding Text, RelativeSource={RelativeSource Self}" Value="No Maintenance Required">
            <Setter Property="Foreground" Value="Gray" /> <!-- special behavior -->
        </DataTrigger>
    </Style.Triggers>
</Style>

In this case, just assign your TextBlock the appropriate Style property.

Second Method: Use an IValueConverter

This approach creates a custom "IValueConverter implementation, which converts your Text value to a Color. From there, we bind directly to our text value, and ensure that the converter always provides the proper color.

public class MaintenaceColorConverter : IValueConverter
{

    public Color NormalColor { get; set; }
    public Color NoMaintenanceRequiredColor { get; set; }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (value.ToString() == "No Maintenance Required")
            return NoMaintenanceRequiredColor;

        return NormalColor;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

In your XAML:

<Window.Resources>
    <local:MaintenaceColorConverter x:Key="myColorConverter" NormalColor="Black" NoMaintenanceRequiredColor="Gray" />
</Window.Resources>

In your TextBlock:

<TextBlock Text="{Binding MaintStatus}" Foreground="{Binding MaintStatus, Converter={StaticResource myColorConverter}}" />

Improvements

With either of these approaches, it would be better to have a MaintenanceStatus boolean or enum value, and bind your styling conditions to that. It's a bad idea to use string-comparisons. That's just begging for trouble. These examples used string comparison because... well... that's all that was available from your provided example code.

Upvotes: 6

paparazzo
paparazzo

Reputation: 45096

More than you asked for but this is from some existing code

<Style TargetType="ListViewItem">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=IsMaintenance}" Value="True">
            <Setter Property="Background" Value="Gainsboro"  />
            <Setter Property="Foreground" Value="Red" />
        </DataTrigger>
        <Trigger Property="IsSelected" Value="True" >
            <Setter Property="FontWeight" Value="Bold" />
        </Trigger>
    </Style.Triggers>
</Style>

Upvotes: 2

Related Questions