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:
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";
}
}
}
}
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!
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.
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}}" />
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.