How to bind textblock.foreground to a variable? (WPF C#) - 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!

Related

How do I pass a binded value from a listview into a function? C# UWP

I am working on a C# UWP assignment and I have a listview displaying information from a database. I want to show a list of orders, and each row has an orderID and a button to delete the order (which will delete it from the database). I though I could use a x:Name="orderid" and just pull that value in my function but it isn't recognizing it in the c# code.
.xaml.cs file includes:
Order o = new Order();
OrderList.ItemsSource = o.GetProducts(user);
.xaml (removed other columns for sake of example):
<RelativePanel Grid.Row="4" Grid.RowSpan="3" Grid.Column="0" Grid.ColumnSpan="7" HorizontalAlignment="Center">
<ListView Name="OrderList"
SelectionMode="Single"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.IsVerticalRailEnabled="True"
ScrollViewer.VerticalScrollMode="Enabled"
ScrollViewer.HorizontalScrollMode="Enabled"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.IsHorizontalRailEnabled="True"
Margin="20">
<ListView.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<TextBlock Text="ID" Margin="8,0" Width="50" Foreground="DarkRed" />
<TextBlock Text="Date" Width="200" Foreground="DarkRed" />
<TextBlock Text="Delete" Width="50"/>
</StackPanel>
</DataTemplate>
</ListView.HeaderTemplate>
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:Order">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="orderid"
Text="{x:Bind OrderID}"
Width="50" />
<TextBlock Name="orderdate"
Text="{x:Bind OrderDate}"
Width="200" />
<Button Content="X" Click="Button_Click"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</RelativePanel>
C# (Order Class - OrderID etc are all properties):
public ObservableCollection<Order> GetProducts(User user)
{
const string GetOrdersQuery = "select * from orders";
var orders = new ObservableCollection<Order>();
try
{
using (SqlConnection conn = new SqlConnection(user.ConnectionString))
{
conn.Open();
if (conn.State == System.Data.ConnectionState.Open)
{
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandText = GetOrdersQuery;
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var order = new Order();
order.OrderID = (int)reader["orderID"];
order.OrderDate = (DateTime)reader["orderDate"];
order.MethodOfDelivery = (string)reader["methodOfDelivery"];
order.DeliveryAddress = (reader["deliveryAddress"] as string);
order.MethodOfPayment = (string)reader["methodOfPayment"];
order.CardUsed = (reader["cardNumber"] as string);
order.Subtotal = (decimal)reader["subtotal"];
order.Discount = (decimal)reader["discountPercentage"];
order.Tax = (decimal)reader["tax"];
order.OrderTotal = (decimal)reader["orderTotal"];
order.CustID = (reader["custID"] as int?) ?? 0;
orders.Add(order);
}
}
}
}
}
return orders;
}
catch (Exception eSql)
{
Debug.WriteLine("Exception: " + eSql.Message);
}
return null;
}
and then I wanted to make a function in the xaml.cs file that would grab that row's orderID, and make a query to delete the order based on the id, but doing orderid.Text won't work here for some reason.
Any suggestions/help would be much appreciated!
If my code is a little all over the place it's because I'm fairly new to this!
One possible solution is to bind the "OrderId" to the "Tag" attribute in your Button element.
<Button Content="X" Click="Button_Click" Tag="{Binding Id}"/>
After the bind, in your Event handler for Click you call a method to remove it from database and from your ObservableCollection, something like:
private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
var button = sender as Button;
_viewModel.DeleteOrder((int)button.Tag);
}
My DeleteOrder method is simple:
private ObservableCollection<Order> _orders;
public ObservableCollection<Order> Orders
{
get { return _orders; }
set { _orders = value; OnChange(); }
}
public void DeleteOrder(int id)
{
var order = Orders.FirstOrDefault(o => o.Id == id);
if (order != null)
{
Orders.Remove(order);
}
}

UWP Object reference not set to an instance of an object when adding item to Azure easy table

I have a popup window that takes in input from a user and then should send it to a model which then POSTS it to an azure easy table. When I build the project everything runs fine until I hit the submit button then the app crashes and I get the Null Exception Object reference not set to an instance of an object.
XAML for input:
<Popup x:Name="ppup" IsOpen="False" IsLightDismissEnabled="True"
Width="320" HorizontalAlignment="Left">
<Popup.ChildTransitions>
<TransitionCollection>
<!--<EdgeUIThemeTransition Edge="Left" />-->
<PaneThemeTransition Edge="Left" />
</TransitionCollection>
</Popup.ChildTransitions>
<Grid Width="380" Height="{Binding ElementName=flyoutPane, Path=Height}" Background="{ThemeResource FlyoutBackgroundThemeBrush}" >
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10,10,10,10" >
<TextBlock Name="NameText" Text="Enter Name:"/>
<TextBox Name="NameBox" Width="200" Height="50"/>
<TextBlock Name="SetsText" Text="Enter Sets:"/>
<TextBox Name="SetsBox" Width="200" Height="50"/>
<TextBlock Name="TimeText" Text="Enter Time to complete:"/>
<TextBox Name="TimeBox" Width="200" Height="50"/>
<Button Name="SubmitBtn" Height="30" Width="100" Content="Submit" Click="SubmitBtn_Click"/>
</StackPanel>
</Grid>
</Popup>
C# for handling input and passing it to model:
CombatTableView ctv = new CombatTableView();
private async void SubmitBtn_Click(object sender, RoutedEventArgs e)
{
DrillItem drillItem = new DrillItem();
String Name = NameBox.Text;
int Sets = Int32.Parse(SetsBox.Text);
int Time = Int32.Parse(TimeBox.Text);
await ctv.combatDrillsTable.AddDrill(drillItem, Name, Sets, Time, parameters);
ppup.IsOpen = false;
var dialog = new MessageDialog("Your message here");
await dialog.ShowAsync();
}
View Model:
class CombatTableView
{
public CombatDrillsTable combatDrillsTable { get; set; }
public CombatTableView()
{
this.combatDrillsTable = new CombatDrillsTable();
}
}
Model for interacting with database:
public async Task AddDrill(DrillItem drillItem, String n, int s, int t, string sty)
{
drillItem.Name = n;
drillItem.Sets = s;
drillItem.SetTime = t;
drillItem.Style = sty;
await App.MobileService.GetTable<DrillItem>().InsertAsync(drillItem);
drills.Add(drillItem);
}
In my case the problem was due to a checkbox accidentally bound to the event instead of the property:
<CheckBox x:Name="chkIsActive" Checked="{Binding IsActive}" />
instead of
<CheckBox x:Name="chkIsActive" IsChecked="{Binding IsActive}" />
There was a null value inside combatDrillsTable and the list that was to hold the objects null too because i hadn't put anything into my easy table at that stage so it was throwing a null exception.

Window.ShowDialog failes on return

I have a custom input dialog box that request an user's name and a reson (because reasons) for doing a certain action in my application. The user clicks on a button on the main window and the dialog box shows, as it should.
The user then enters his/her name and the reason and clicks ok. The dialog then closes but I ( the program) never receives an answer. Here is my XAML for the input dialog:
<Window x:Class="Sequence_Application_2.GUI.ForcedRackInput"
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"
Title="Forcera Rack" Height="300" Width="300"
WindowStartupLocation="CenterScreen">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="21*"/>
<ColumnDefinition Width="274*"/>
</Grid.ColumnDefinitions>
<TextBox Name="OperatorNameText" HorizontalAlignment="Left" Height="23" Margin="15,36,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" Grid.ColumnSpan="2"/>
<Label x:Name="label" Content="Namn:" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2"/>
<Label x:Name="label1" Content="Orsak:" HorizontalAlignment="Left" Margin="10,72,0,0" VerticalAlignment="Top" Grid.ColumnSpan="2"/>
<Border BorderThickness="1" BorderBrush="Black" Grid.ColumnSpan="2" Margin="0,0,0,0.5">
<TextBox Name="ReasonText" Margin="15,98,15,0" TextWrapping="Wrap" VerticalAlignment="Top" Height="116" />
</Border>
<Button Name="OkButton" IsDefault="True" Content="OK" Click="OkButtonPressed" HorizontalAlignment="Left" Margin="26.202,233,0,0" VerticalAlignment="Top" Width="75" Cursor="Arrow" Grid.Column="1"/>
<Button Name="CancelButton" IsCancel="True" Content="Avbryt" Margin="152.202,233,47,0" VerticalAlignment="Top" Cursor="Arrow" Grid.Column="1"/>
</Grid>
</Window>
And here is the "behind code":
namespace Sequence_Application_2.GUI
{
using System.Windows;
public partial class ForcedRackInput : Window
{
public string OperatorName { get { return OperatorNameText.Text; } }
public string ForcedRackReason { get { return ReasonText.Text; } }
public ForcedRackInput()
{
InitializeComponent();
}
private void OkButtonPressed(object sender, RoutedEventArgs e)
{
this.DialogResult = true;
}
}
}
and this is how I call the code (from a model, not a "window class")
public void ForceClosingRack(Flow forcedFlow)
{
var forcedRackWindow = new ForcedRackInput();
string operatorName = "";
string reasonForForced = "";
if( forcedRackWindow.ShowDialog() == true)
{
operatorName = forcedRackWindow.OperatorName;
reasonForForced = forcedRackWindow.ForcedRackReason;
}
} // code jumps from "if(forcedRackWindow.... to this line when ok is clicked in the dialog
Looked for the solution for some time now and I just about to change career
Thanks for your time
My guess is that the problem doesn't lie in the code, which seems to be fine, but in your if statement.
When you run your program in Debug mode it should work as expected.
My guess is that you are assigning the variables operatorName and reasonForForced inside your if statment but they are not used anywhere else in the program and hence the whole if statement is ignored by the compiler and not present when running in Release mode.
A small modification in your code which embeds different behaviour depending on the variable values can prove my guess:
private void Button_Click(object sender, RoutedEventArgs e)
{
var forcedRackWindow = new ForcedWindow();
string operatorName = "foo";
string reasonForForced = "foo";
if (forcedRackWindow.ShowDialog() == true)
{
operatorName = forcedRackWindow.OperatorName;
reasonForForced = forcedRackWindow.ForcedRackReason;
}
if(!operatorName.Equals(reasonForForced))
{
MessageBox.Show("We are not the same");
}
}

Basic WPF validation and DataBinding

I'm just getting started with WPF and particularly Validations and DataBinding.
This is my XAML code
<Window x:Class="simpledatagrid.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="IDDATA" Height="350" Width="525">
<Grid >
<DataGrid SelectionChanged="Iddetails" Name="dgsample" BorderBrush="Black" BorderThickness="2" AutoGenerateColumns="True" CanUserAddRows="True" CanUserDeleteRows="True" CanUserSortColumns="False" Margin="200,10,10,75"></DataGrid>
<Label Content="ID :" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Height="26" Width="27"/>
<Label Content="Name :" HorizontalAlignment="Left" Margin="10,60,0,0" VerticalAlignment="Top" Height="26" Width="48"/>
<Label Content="Salary :" HorizontalAlignment="Left" Margin="10,110,0,0" VerticalAlignment="Top" Height="26" Width="47"/>
<TextBox Name="tb1" HorizontalAlignment="Left" Height="20" Margin="60,10,0,0" TextWrapping="NoWrap" Text="" VerticalAlignment="Top" Width="100" />
<TextBox Name="tb2" HorizontalAlignment="Left" Height="20" Margin="60,60,0,0" TextWrapping="NoWrap" Text="" VerticalAlignment="Top" Width="100"/>
<TextBox Name="tb3" HorizontalAlignment="Left" Height="20" Margin="60,110,0,0" TextWrapping="NoWrap" Text="" VerticalAlignment="Top" Width="100"/>
<Button Content="Get" HorizontalAlignment="Left" Margin="10,190,0,0" VerticalAlignment="Top" Width="75" Click="Get_Click" />
<Button Content="Add" HorizontalAlignment="Left" Margin="10,230,0,0" VerticalAlignment="Top" Width="75" Click="Add_Click" />
<Button Content="Delete" HorizontalAlignment="Left" Margin="10,270,0,0" VerticalAlignment="Top" Width="75" Click="Delete_Click" />
</Grid>
This is my .CS code
public partial class MainWindow : Window
{
ObservableCollection<User> Users = new ObservableCollection<User>();
public MainWindow()
{
InitializeComponent();
Users.Add(new User() { Id = 101, Name = "leon", Salary = 10 });
Users.Add(new User() { Id = 102, Name = "allen", Salary = 20 });
Users.Add(new User() { Id = 103, Name = "neon", Salary = 30 });
Users.Add(new User() { Id = 104, Name = "xeln", Salary = 40 });
Users.Add(new User() { Id = 105, Name = "kalen", Salary = 50 });
Users.Add(new User() { Id = 106, Name = "velen", Salary = 60 });
dgsample.ItemsSource = Users;
}
private void Iddetails(object sender, SelectionChangedEventArgs args)
{
int index = dgsample.SelectedIndex;
tb1.Text = Users[index].Id.ToString();
tb2.Text = Users[index].Name;
tb3.Text = Users[index].Salary.ToString();
}
private void Get_Click(object sender, RoutedEventArgs e)
{
int index;
if (int.TryParse(this.tb1.Text, out index))
{
User currentUser = Users.FirstOrDefault(Select => Select.Id == int.Parse(tb1.Text));
if (currentUser != null)
{
this.tb2.Text = currentUser.Name;
this.tb3.Text = currentUser.Salary.ToString();
}
else
MessageBox.Show("User with the provided ID does not Exist", "Error");
}
else
MessageBox.Show("ID entered is not valid number", "Error");
}
private void Add_Click(object sender, RoutedEventArgs e)
{
if (!tb1.Text.Equals(""))
{
var adduser = Users.Where(User => User.Id == int.Parse(tb1.Text));
if (!adduser.Any())
{
Users.Add(new User() { Id = int.Parse(tb1.Text), Name = tb2.Text, Salary = int.Parse(tb3.Text) });
}
else
MessageBox.Show("Someone already has that ID.");
}
}
private void Delete_Click(object sender, RoutedEventArgs e)
{
int index;
if (int.TryParse(this.tb1.Text, out index))
{
User currentUser = Users.FirstOrDefault(Select => Select.Id == int.Parse(tb1.Text));
if (currentUser != null)
{
Users.Remove(currentUser);
}
else
MessageBox.Show("User with the provided ID does not Exist", "Error");
}
else
MessageBox.Show("ID entered is not valid number", "Error");
}
}
This code is working but i need the same thing using the concept of DataBinding and Validations for TextBoxes,Please help me with the required code
this may help you
<Binding Path="EventDate" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
you can refer to this link also
http://www.codeproject.com/Articles/29054/WPF-Data-Binding-Part-1
Well you would have some work rewriting it using DataBinding that is the general overview on creating data bindings for example:
<TextBox Name="tb3" HorizontalAlignmentText="{Binding Path=SelectedIndex, ElementName=Grid, , Mode=OneWay}"/>
So let me explain, I have binded Text property of TextBox tb3 to the following property SelctedIndex of element Grid using following mode OneWay which means, changing the selected index in the Grid will affect tb3 Text but changing the Text in the textbox will not affect actual Grid selection. Sometimes when the property types doesn't match you have to use a converter here is an example:
msdn.microsoft.com
In general it looks pretty much like this, but note that you could also bind the propierties in code-behind but if you can better stay to xaml.
Tip: If you want to use bindings you have to make sure that the Path points at property.
Finally here are the links for more information about validation you should check out:
NEW: www.codeproject.com
SO question
blogs.msdn.com
Or code-behind (not recommended)
private void tb1_TextChanged(object sender, TextChangedEventArgs e)
{
int output;
if (!int.TryParse(tb1.Text, out output))
{
MessageBox.Show("Enter valid int.");
tb1.Text = "0";
}
}
Useful link about DataBinding:
msdn.microsoft.com
Here I provide you with binding and converter for tb2 TextBox, to display currently selected user name:
Add this class to your namespace:
using System;
using System.Globalization;
using System.Windows.Data;
namespace WpfApplicationTest
{
[ValueConversion(typeof(object), typeof(String))]
public class MyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string name = "";
if (value != null)
{
User user = (User)value;
name = user.Name;
}
else
{
name = "";
}
return name;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}
Then add this to you window:
xmlns:myNamespace="clr-namespace:WpfApplicationTest"
<Window.Resources>
<myNamespace:MyConverter x:Key="myConverter"/>
</Window.Resources>
Then edit your TextBox like this:
<TextBox Name="tb2" Text="{Binding Path=SelectedItem, ElementName=Grid, Mode=OneWay, Converter={StaticResource myConverter}}"/>
What you need to do first ist read about MVVM.
You seem to be using wpf as winforms which sucks big time.
When you done reading about mvvm (let say in a week.. or so), read those articles.
IDataErrorInfo is what you looking for:
http://codeblitz.wordpress.com/2009/05/08/wpf-validation-made-easy-with-idataerrorinfo/
http://tarundotnet.wordpress.com/2011/03/03/wpf-tutorial-how-to-use-idataerrorinfo-in-wpf/

WPF Listview Groups Headers Not Displaying: Any Ideas?

Below is the XAML. The data from the object is populating the listview. I just can't get the groups to display. I want to group on a property called workcenter, which isn't displayed in the listview, but it's still part of the class bound to it.
I'm a WPF noob, so forgive me if the answer is right in front of me and I'm missing it. I'm using examples from the web to try to get this to work but it's just not to this point.
This is a mock-up for a project I have to do, so ignore the silly form text and namespace. =)
<Window x:Class="Son_of_a_Batching_WPF_Mock_up.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="You Son of a Batch. I'm Detective John Kimble! I'm A Cop You Idiot!" Height="600" Width="800">
<Window.Resources>
<CollectionViewSource Source="{Binding testDataCollection}"
x:Key="src">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="workcenter" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid x:Name="mainGrid" Background="White">
<ComboBox x:Name="cboWorkCenters" HorizontalAlignment="Left" VerticalAlignment="Top" Width="256" Background="{x:Null}" Margin="12,50,0,0" SelectedIndex="0" FontSize="12pt" Height="27.28">
<ComboBoxItem Content="Group by work center" FontSize="12pt"/>
<ComboBoxItem Content="Group by batch type" FontSize="12pt"/>
</ComboBox>
<Label x:Name="lblFilter" FontSize ="12pt" HorizontalAlignment="Left" Margin="11.473,13.043,0,0" VerticalAlignment="Top" Content="Filter by work center:" Width="252.527" FontWeight="Bold"/>
<ListView x:Name="lvBatches" Margin="12,83,12,12" ItemsSource="{Binding Source = {StaticResource src}}"><!--ItemsSource="{Binding testDataCollection}">-->
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Margin" Value="0,0,0,5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander IsExpanded="True" BorderBrush="#FFA4B97F" BorderThickness="0,0,0,1">
<Expander.Header>
<DockPanel>
<TextBlock FontWeight="Bold" Text=" {Binding Path=name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text=" {Binding Path=itemcount}"/>
</DockPanel>
</Expander.Header>
<Expander.Content>
<ItemsPresenter />
</Expander.Content>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn x:Name="colID" Header="Batch ID" Width="200" DisplayMemberBinding="{Binding id}"/>
<!--<GridViewColumn x:Name="colWC" Header="Work Center" Width="100" DisplayMemberBinding="{Binding workcenter}"/>-->
<GridViewColumn x:Name="colStart" Header="Start Time" Width="150" DisplayMemberBinding="{Binding start}"/>
<GridViewColumn x:Name="colEnd" Header="End Time" Width="150" DisplayMemberBinding="{Binding end}"/>
<GridViewColumn x:Name="colDur" Header="Duration" Width="100" DisplayMemberBinding="{Binding duration}"/>
<GridViewColumn x:Name="colBatchType" Header="Batch Type" Width="100" DisplayMemberBinding="{Binding batchtype}"/>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
Since I can't post images, I'll try to map out how it looks using text skills. What I get is this:
**Batch ID Start Time End Time Duration Batch Type**
-------- ---------- -------- -------- ----------
12344555 7/21/11 7/22/11 100 Loaded
54564564 7/21/11 7/23/11 50 Sequential
12433555 7/21/11 7/22/11 100 Loaded
54564564 7/21/11 7/23/11 50 Sequential
12311555 7/21/11 7/22/11 100 Loaded
54456564 7/21/11 7/23/11 50 Sequential
12344555 7/21/11 7/22/11 100 Loaded
57744564 7/21/11 7/23/11 50 Sequential
12994555 7/21/11 7/22/11 100 Loaded
54500564 7/21/11 7/23/11 50 Sequential
What I want is this, with 332, 404 being the groups.
**Batch ID Start Time End Time Duration Batch Type**
-------- ---------- -------- -------- ----------
**332**
12344555 7/21/11 7/22/11 100 Loaded
12433555 7/21/11 7/22/11 100 Loaded
12311555 7/21/11 7/22/11 100 Loaded
**404**
54564564 7/21/11 7/23/11 50 Sequential
54564564 7/21/11 7/23/11 50 Sequential
54500564 7/21/11 7/23/11 50 Sequential
Here's the code behind. I don't think it'll help. Most of it is just test data to display in the mock-up.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Collections.ObjectModel;
namespace Son_of_a_Batching_WPF_Mock_up
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
private ObservableCollection<TestData> _testDataCollection = new ObservableCollection<TestData>();
public ObservableCollection<TestData> testDataCollection { get { return _testDataCollection; } }
//public System.ComponentModel.ICollectionView source { get; set; }
public Window1()
{
InitializeComponent();
//this.source.GroupDescriptions.Add(new PropertyGroupDescription("workcenter"));
LoadTestData();
}
private void LoadTestData()
{
//int[] icnt = new int[529];
for (int i = 0; i <= 99; i++)
{
_testDataCollection.Add(new TestData());
}
//for (int i = 0; i < _testDataCollection.Count; i++)
//{
// icnt[int.Parse(_testDataCollection[i].workcenter)] ++;
//}
//this.label1.Content = "332:" + icnt[332].ToString() + "," + "402:" + icnt[402].ToString() + ","+ "404:" + icnt[404].ToString() + ","
// + "522:" + icnt[522].ToString() + ","+ "523:" + icnt[523].ToString() + ","+ "524:" + icnt[524].ToString() + ","
// + "527:" + icnt[527].ToString() + ","+ "528:" + icnt[528].ToString() + ",";
}
}
public class TestData
{
private string[] _WCs = new string[] { "404", "527", "523", "524", "332", "528", "522", "402" };
private string[] _workcenters = new string[1000];
private string _workcenter;
private double _duration = 0;
public string id
{
get { return Guid.NewGuid().ToString(); }
}
public string workcenter
{
get
{
return _workcenter;
}
}
public DateTime start
{
get { return DateTime.Now; }
}
public DateTime end
{
get { return DateTime.Now; }
}
public double duration
{
get
{
return _duration;
}
}
public string batchtype
{
get
{
switch (workcenter)
{
case "332":
case "402":
case "527":
return "Loaded Batch";
case "404":
case "524":
return "Sequential Batch";
case "522":
case "528":
case "523":
return "Supervisor Batch";
default:
return "";
}
}
}
public TestData()
{
RandomizeWCs();
Random rnd = new Random();
_workcenter = _workcenters[rnd.Next(0, 999)];
rnd = new Random();
_duration = rnd.Next(10, 60);
}
private void RandomizeWCs()
{
Random rnd = new Random();
int iIndex, i;
while (_workcenters.Contains(null))
{
iIndex = rnd.Next(1000);
i = rnd.Next(8);
if (_workcenters[iIndex] == null)
{
_workcenters[iIndex] = _WCs[i];
}
}
}
}
}
Unreal: I've been banging my head against the wall for hours and I'm embarrassed to say I solved my own problem. The solution?
<TextBlock FontWeight="Bold" Text=" {Binding Path=name}" Margin="5,0,0,0" Width="100"/>
<TextBlock FontWeight="Bold" Text=" {Binding Path=itemcount}"/>
Freaking name and itemcount weren't capitalized!!! Give me a second to punch myself in the face...annnnnd...I'm back now. Name and ItemCount are the proper way to code that.
Thanks anyway for the help all who responded.

Categories

Resources