Data BindingGroup to Datatable using 4.5 .NET framework - c#

I am trying to use binding group to validate all my fields in a datatable and have managed to find an example project to download to use to implement into my own project. MY problem was that everytime I added the code to my own code I was getting a Object reference not set to an instance of an object error in my validation result code whereas the sample project did not. I have managed to isolate it down through trial and error to the projects properties .NET setting but cannot figure out how to get passed my problem. The code below code works in .NET 3.5 but not in .NET 4.5 if anyone can figure out my problem.
thanks Callum
<Window x:Class="WPFTypeValidation.GroupValidation"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFTypeValidation"
Title="Validation Demo" Height="130" Width="200">
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Height" Value="20"/>
<Setter Property="VerticalAlignment" Value="Top"/>
</Style>
</Window.Resources>
<Border Padding="5">
<Grid x:Name="RootElement" >
<Grid.BindingGroup>
<BindingGroup>
<BindingGroup.ValidationRules>
<local:PersonValidationRule
ValidationStep="UpdatedValue"/>
</BindingGroup.ValidationRules>
</BindingGroup>
</Grid.BindingGroup>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="1.8*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Content="Name:"/>
<TextBox Grid.Column="1" LostFocus="TextBox_LostFocus"
Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
<Label Grid.Row="1" Content="Age:"/>
<TextBox Grid.Row="1" Grid.Column="1" LostFocus="TextBox_LostFocus"
Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}"/>
<Label Grid.Row="2" Grid.ColumnSpan="2" Foreground="Red"
Content="{Binding Path=(Validation.Errors)[0].ErrorContent, ElementName=RootElement}"/>
</Grid>
</Border>
using System;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
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;
namespace WPFTypeValidation
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class GroupValidation : Window
{
private DataTable FirstMenuDT;
public GroupValidation()
{
InitializeComponent();
FirstMenuDT = new DataTable();
FirstMenuDT.Columns.Add("Name");
FirstMenuDT.Columns.Add("Age");
FirstMenuDT.Rows.Add("Fantastic Mr. Fish", 34);
RootElement.DataContext = FirstMenuDT;
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
RootElement.BindingGroup.CommitEdit();
}
}
public class PersonValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var dataRowView = (value as BindingGroup).Items[0] as DataRowView;
string Initial;
var statusEx = new Regex(#"^[A-Z ]+$");
Initial = dataRowView.Row[0].ToString();
if (Initial == string.Empty)
{
return new ValidationResult(false, "Initial cannot be empty.");
}
if (!statusEx.Match(Initial).Success)
{
return new ValidationResult(false,
"Initials column must be in uppercase alphabetic characters only.");
}
if (Initial.Length > 3)
{
return new ValidationResult(false,
"Maximum of 3 characters only.");
}
return new ValidationResult(true, null);
}
}
}

Related

C# WPF Binding String to Textblock [duplicate]

This question already has answers here:
Why does WPF support binding to properties of an object, but not fields?
(2 answers)
Closed last month.
I am still a beginner and I'm trying to make a application where a user gets to choose a file. For that I use OpenFileDialog, which has worked fine for me in the past. The file path and content should then be each displayed in a separate textbox. I tried to achieve this by storing the content and path in two strings. Because I want to use those strings later on in my project in another .cs file I figured I make them globally accessable by defining them as public string content = "No file selected yet;. The "No file selected yet" should be the default text before the user has selected anything.
I searched online and tried to accomplish my goals by using DataContext and Binding, but I can't figure out how to make it work. This is the code I have right now:
xaml file:
<UserControl x:Class="Project_Neon.MVVM.View.ChooseFileView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Project_Neon.MVVM.View"
mc:Ignorable="d"
d:DesignHeight="423.4"
d:DesignWidth="548.4">
<StackPanel Background="White">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="70"/>
<RowDefinition Height="100"/>
<RowDefinition Height="35"/>
<RowDefinition Height="218.4"/>
</Grid.RowDefinitions>
<TextBlock Grid.Column="1"
Grid.ColumnSpan="2"
Margin="0,5,20,0"
Text="Choose File"
Foreground="Black"
FontSize="28"
FontFamily="/Fonts/#Kanit Semi"/>
<TextBlock Text="Select the text document with the cheat sheet.
Only Documents in a .txt format can be accepted."
Foreground="Black"
FontSize="22"
FontFamily="/Fonts/#Kanit Light"
Grid.Row="1"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="0,0,20,10"/>
<Button Grid.Row="2"
Grid.Column="1"
VerticalAlignment="Top"
Content="Select file"
Height="25"
Width="70"
FontFamily="/Fonts/#Kanit"
FontSize="14"
Style="{StaticResource SelectFileButtonTheme}"
Click="SelectFileButton_Click"/>
<Grid Grid.Row="2"
Grid.Column="2"
Height="25"
VerticalAlignment="Top"
Margin="0,0,20,0">
<Border Padding="10"
BorderBrush="#1f1f1f"
BorderThickness="1">
</Border>
<TextBlock Name="FilePathDisplay"
Text="{Binding Path=CheatFilePath}"
VerticalAlignment="Center"
Margin="5,0,0,0"
FontSize="14"
FontFamily="/Fonts/#Kanit Light"/>
</Grid>
<Grid Grid.Row="3"
Grid.Column="1"
Grid.ColumnSpan="2"
Margin="0,0,20,20">
<TextBlock Height="20"
VerticalAlignment="Top"
Background="#1f1f1f"
Foreground="White"
Text=" Preview:"
FontSize="14"
FontFamily="/Fonts/#Kanit"/>
<Border Padding="10"
BorderBrush="#1f1f1f"
BorderThickness="1"
Margin="0,20,0,0">
<ScrollViewer HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" Margin="-10,-10,-10,-10">
<TextBlock Width="Auto"
TextWrapping="Wrap"
Text="{Binding Path=CheatFileContent}"
VerticalAlignment="Top"
FontSize="13"
Margin="1.5,0,1.5,0"
FontFamily="/Fonts/#Kanit Light"/>
</ScrollViewer>
</Border>
</Grid>
</Grid>
</StackPanel>
</UserControl>
and the xaml.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
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.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Project_Neon.MVVM.View
{
/// <summary>
/// Interaction logic for ChooseFileView.xaml
/// </summary>
public partial class ChooseFileView : System.Windows.Controls.UserControl
{
public ChooseFileView()
{
InitializeComponent();
this.DataContext = this;
}
public string CheatFileContent = "No file selected yet";
public string CheatFilePath = "No file selected yet";
private void SelectFileButton_Click(object sender, RoutedEventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = "c:\\";
openFileDialog.Filter = ".txt (*.txt)|*.txt";
openFileDialog.FilterIndex = 1;
openFileDialog.Multiselect = false;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
var CheatFileStream = openFileDialog.OpenFile();
using (StreamReader reader = new StreamReader(CheatFileStream))
{
CheatFileContent = reader.ReadToEnd();
CheatFilePath = openFileDialog.FileName;
OnPropertyChanged(nameof(CheatFilePath));
OnPropertyChanged(nameof(CheatFileContent));
}
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I'm afraid that maybe my strings aren't defined right.
(I deleted most of the code from my previous attempts of actually binding the data, because I only got errors.)
As #Andy pointed out you need to bind to properties not fields (check #Marc Gravell answer)
I included the raising of the event inside the setter as it makes more sense
ChooseFileView.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Cryptography.X509Certificates;
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.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Project_Neon.MVVM.View
{
/// <summary>
/// Interaction logic for ChooseFileView.xaml
/// </summary>
public partial class ChooseFileView : System.Windows.Controls.UserControl, System.ComponentModel.INotifyPropertyChanged
{
private string _cheatFileContent;
private string _cheatFilePath;
public event PropertyChangedEventHandler? PropertyChanged;
public string CheatFileContent
{
get => _cheatFileContent;
set
{
if (_cheatFileContent != value)
{
_cheatFileContent = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CheatFileContent)));
}
}
}
public string CheatFilePath
{
get => _cheatFilePath;
set
{
if (_cheatFilePath != value)
{
_cheatFilePath = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CheatFilePath)));
}
}
}
public ChooseFileView()
{
InitializeComponent();
this.DataContext = this;
CheatFileContent = "No file selected yet";
CheatFilePath = "No file selected yet";
}
private void SelectFileButton_Click(object sender, RoutedEventArgs e)
{
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = "c:\\";
openFileDialog.Filter = ".txt (*.txt)|*.txt";
openFileDialog.FilterIndex = 1;
openFileDialog.Multiselect = false;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
var CheatFileStream = openFileDialog.OpenFile();
using (StreamReader reader = new StreamReader(CheatFileStream))
{
CheatFileContent = reader.ReadToEnd();
CheatFilePath = openFileDialog.FileName;
}
}
}
}
}
}

Getting image file path from PreviewMouseDown event (WPF/C#)

I'm making an image viewer. I have a mouse event that is supposed to trigger when an image (or child of StackPanel in this case), is clicked. The event is triggered as intended. The problem is, since the pathfile of the images is different depending on whichever is selected (in which directory, etc.), I just don't know how to get the pathfile of the specific image that is currently clicked on. The directory depends on whichever folder is loaded, so the pathfile could be different everytime.
If you scroll all the way down to Image_Load(), I have the Pic.Fill which is a reference to the rectangle that is being filled with the image. I'm trying to find the first parameter for Uri(_,_), which is supposed to be where the #"path" of the current image is.
using System;
using System.IO;
using Microsoft.Win32;
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.Drawing;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfApp1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
FolderBrowserDialog fbd = new FolderBrowserDialog();
List<string> paths = new List<string>();
string[] extension = new[] { ".jpg", ".png", ".jpeg", ".gif", ".bmp", ".tif", ".tiff" };
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e) {
if (fbd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string[] files = Directory.GetFiles(fbd.SelectedPath);
FileLocation.Text = fbd.SelectedPath;
}
}
private void OpenButton_Click(object sender, RoutedEventArgs e)
{
LoadPhotos();
}
private void LoadPhotos()
{
try
{
DirectoryInfo di = new DirectoryInfo(fbd.SelectedPath);
foreach (var fi in di.GetFiles().Where(f => extension.Contains(f.Extension.ToLower())).ToArray())
{
paths.Add(fi.FullName);
}
Photos.ItemsSource = paths;
} catch (Exception e)
{
Console.WriteLine("File not found.", e.Source);
}
}
private void Image_Load(object sender, MouseButtonEventArgs e)
{
// What is the reference here?
Pic.Fill = new ImageBrush { ImageSource = new BitmapImage(new Uri(/*Insert pathfile here*/, UriKind.Absolute)) };
}
}
}
Here's my .xaml. I've analyzed it to see a reference but the only thing that stands out is the Image Source, which I've tagged with a comment. It's set to { Binding . } which I'm going to assume means, whatever image is referenced. But how do I connect that variable reference to my Image_Load()? (which is declared on the instantiation of ListBox)
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="1081" Width="1720.5">
<Grid Margin="0,0,2,-0.578">
<Button x:Name="OpenButton" Content="Load" HorizontalAlignment="Left" Height="44" Margin="1166.374,932.042,0,0" VerticalAlignment="Top" Width="145.666" Background="#FFDDDDDD" FontSize="24" Click="OpenButton_Click"/>
<Button x:Name="BrowseButton" Content="Browse" HorizontalAlignment="Left" Height="44" Margin="58.666,932.042,0,0" VerticalAlignment="Top" Width="134.166" Background="#FFDDDDDD" FontSize="24" Click="Button_Click"/>
<Rectangle x:Name="Pic" Fill="#FFF4F4F5" HorizontalAlignment="Left" Height="878.767" Margin="58.666,34.733,0,0" Stroke="Black" VerticalAlignment="Top" Width="1253.374"/>
<ScrollViewer VerticalScrollBarVisibility="Visible" Width="327.46" HorizontalAlignment="Left" Margin="1339.04,34.733,0,137.078" RenderTransformOrigin="0.5,0.5" >
<StackPanel Width="327.46" RenderTransformOrigin="0.516,0.496" HorizontalAlignment="Left" VerticalAlignment="Center" Height="878.085">
<ListBox x:Name="Photos"
Grid.Row="2" Grid.Column="1" Height="876" PreviewMouseDown="Image_Load">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Width="300" Height="100">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
/* Image Source? */
<Image Source="{Binding .}" Grid.Column="0"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</ScrollViewer>
<TextBlock x:Name="FileLocation" HorizontalAlignment="Left" Height="44" Margin="192.832,948.542,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="973.542" Foreground="#FF515151" TextAlignment="Center" FontSize="14"/>
</Grid>
</Window>
I've been learning on the way using Blend-- some youtube tutorials here and there. I've tried to look for a solution online but all the questions about it usually involve a static and specific directory. This is my first program and the only thing I need is that darn path file reference. There are so many object reference so I just don't even know how to wrap my head around it, being so unfamiliar with these classes. Some assistance would be very much appreciated! :)

C# WPF image load like progressbar

I'v a progressbar and an image.
When Progress Bar value is 50, image loaded by 50%.
I tried to add image as the progressbar foreground, but it have green shade. So ugly.
How can I do this?
To run this sample, you need a snake image which you can get from http://res.freestockphotos.biz/pictures/16/16242-illustration-of-a-green-snake-pv.png. I have used this url directly, but your should download image first and then use it.
You need a control template for your ProgressBar because you want to show percentage status too.
Otherwise normal ProgressBar would do.
Code can be used as is :
<Window x:Class="WpfControlTemplates._32794074.Win32794074"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Win32794074" Height="600" Width="1000">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="397*"/>
<RowDefinition Height="173*"/>
</Grid.RowDefinitions>
<ProgressBar x:Name="PBarCustom" Width="958" Height="200" Maximum="958" Value="958" Foreground="#FFE6E61F" Margin="17,185,17,11.932" ValueChanged="PBarCustom_ValueChanged">
<ProgressBar.Background>
<ImageBrush ImageSource="http://res.freestockphotos.biz/pictures/16/16242-illustration-of-a-green-snake-pv.png"/>
</ProgressBar.Background>
<ProgressBar.Template>
<ControlTemplate>
<Grid Background="{TemplateBinding Background}">
<Rectangle x:Name="Thumb" HorizontalAlignment="Left" Fill="#FFC5EA1F" Stroke="#FF0DB442" Width="{TemplateBinding Width}" />
<Ellipse Fill="#FF7DEEDE" Height="124" Stroke="#FF0DB442" Width="150" VerticalAlignment="Center" HorizontalAlignment="Center" Opacity="0.3"/>
<Label x:Name="tbStatus" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" FontWeight="Bold" FontSize="75" Foreground="#FF21BD76" Content="0" />
</Grid>
</ControlTemplate>
</ProgressBar.Template>
</ProgressBar>
<Button x:Name="BtnLoadSnake" Content="Load Snake" HorizontalAlignment="Left" Margin="462,14.068,0,0" VerticalAlignment="Top" Width="75" Click="BtnLoadSnake_Click" Grid.Row="1"/>
</Grid>
</Window>
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.Shapes;
using System.Windows.Threading;
namespace WpfControlTemplates._32794074
{
/// <summary>
/// Interaction logic for Win32794074.xaml
/// </summary>
public partial class Win32794074 : Window
{
public Win32794074()
{
InitializeComponent();
}
DispatcherTimer timer;
private void BtnLoadSnake_Click(object sender, RoutedEventArgs e)
{
BtnLoadSnake.IsEnabled = false;
PBarCustom.Value = PBarCustom.Maximum;
Rectangle thumb = (Rectangle)PBarCustom.Template.FindName("Thumb", PBarCustom);
thumb.Width = PBarCustom.Value;
Label status = (Label)PBarCustom.Template.FindName("tbStatus", PBarCustom);
status.Content = ((int)(100 - ((100 * PBarCustom.Value) / PBarCustom.Maximum))).ToString();
Dispatcher disp = PBarCustom.Dispatcher;
EventHandler pBarCallbackHandler = new EventHandler(pBarCallback);
timer = new DispatcherTimer(TimeSpan.FromSeconds(0.5), DispatcherPriority.Normal, pBarCallback, disp);
}
private void pBarCallback(object sender, EventArgs e)
{
PBarCustom.Value -= 13;
Rectangle thumb = (Rectangle)PBarCustom.Template.FindName("Thumb", PBarCustom);
thumb.Width = PBarCustom.Value;
Label status = (Label)PBarCustom.Template.FindName("tbStatus", PBarCustom);
status.Content = ((int)(100 - ((100 * PBarCustom.Value) / PBarCustom.Maximum))).ToString();
if (PBarCustom.Value == 0)
timer.Stop();
}
private void PBarCustom_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if(e.NewValue == 0)
BtnLoadSnake.IsEnabled = true;
}
}
}
i had similar scenario.
if you want the scroll bar to be just a rectangle,
easiest way to make it:
1- add an image to your window.
2- put a grid on it in such a way that the grid hides the image.
3- programatically change the width or height of the grid.
tell me if you needed an example code.

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:
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!
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.
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>
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!

Combobox not visible to code behind in Silverlight

I have been trying to add items to a comboxbox but cannot seem to get the code behind file to recognize the combobox added to the xaml. I am pretty sure I am missing something simple. Basically the xaml here illustrates an empty combobox. The code behind executes the service, waits for json to come back and deserialize it. Unfortunately I cannot get
xaml:
<navigation:Page x:Class="Growing.Views.Room"
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"
mc:Ignorable="d"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
d:DesignWidth="950" d:DesignHeight="480"
Title="Home" Style="{StaticResource PageStyle}" DataContext="{Binding}" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
<Grid x:Name="LayoutRoot" ShowGridLines="True" Background="#FF631C00">
<Grid.ColumnDefinitions>
</Grid.ColumnDefinitions>
<Rectangle Height="298" HorizontalAlignment="Left" Margin="195,94,0,0" Name="rect" Stroke="Black" StrokeThickness="2" VerticalAlignment="Top" Width="582" Fill="#FFAAAAAA" RadiusY="0.25" RadiusX="0.25" />
<sdk:Label Height="38" HorizontalAlignment="Left" Margin="387,160,0,0" Name="label1" VerticalAlignment="Top" Width="203" Content="Select a Room" FontSize="24" FontWeight="Bold" />
<sdk:Label Height="18" HorizontalAlignment="Left" Margin="312,240,0,0" Name="label2" VerticalAlignment="Top" Width="69" Content="Area:" FontSize="14" />
<ComboBox x:Name="RoomAreas" Height="23" HorizontalAlignment="Left" Margin="418,235,0,0" VerticalAlignment="Top" Width="209" />
</Grid>
The code behind:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Runtime.Serialization.Json;
using System.ServiceModel.Web;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Navigation;
using Growing.DataConnectionRef;
namespace Growing.Views
{
public partial class Room : Page
{
public Room()
{
InitializeComponent();
//Asynchronously call the EndReceive Web Service to change the status of an existing open lot record
WebClient GRService = new WebClient();
GRService.DownloadStringCompleted += new DownloadStringCompletedEventHandler(GRService_DownloadStringCompleted);
GRService.DownloadStringAsync(new Uri("/servicestack/GetAreas", UriKind.Relative));
}
static void GRService_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
List<Area> dataList = new List<Area>();
MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(e.Result));
DataContractJsonSerializer ser = new DataContractJsonSerializer(dataList.GetType());
dataList = ser.ReadObject(memoryStream) as List<Area>;
memoryStream.Close();
RoomAreas.ItemSource = dataList;
}
}
}
At RoomAreas.ItemSource I get an error An object reference is required for the non-static field, method, or property 'Growing.Views.Room.RoomAreas'
Sorry if this is hard to follow. Anyone have any ideas what might be going on here?
Thank you in advance!
Make GRService_DownloadStringCompleted method non static:
void GRService_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
List<Area> dataList = new List<Area>();
MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(e.Result));
DataContractJsonSerializer ser = new DataContractJsonSerializer(dataList.GetType());
dataList = ser.ReadObject(memoryStream) as List<Area>;
memoryStream.Close();
RoomAreas.ItemSource = dataList;
}
Try adding ItemsSource="{Binding}" to the combobox property within the Xaml

Categories

Resources