AutoCompleteBox's Dropdownlist items is not displayed in WPF - wpf

I am new to WPF.I have AutoCompleteBox.When i enter a search text,dropdownlist is populated.It contains items.I am able to selected those items and saved into databse through down or up arrow.But items are not visible.Here is my code
<AutoComplete:AutoCompleteBox Background="White" Tag="TagName..." Margin="0,0,28.8,0" Name="txtCustomTagName" BorderBrush="#FF104E8B" FontWeight="Normal" BorderThickness="1,1,0,1" FontSize="14" Foreground="#FF104E8B" TextChanged="txtCustomTagName_TextChanged" LostFocus="txtCustomTagName_LostFocus" PreviewTextInput="txtCustomTagName_PreviewTextInput" Populating="txtCustomTagName_Populating" >
<AutoComplete:AutoCompleteBox.ItemTemplate>
<DataTemplate>
<TextBlock />
</DataTemplate>
</AutoComplete:AutoCompleteBox.ItemTemplate>
</AutoComplete:AutoCompleteBox>
//Populated Event:-
private void txtCustomTagName_Populating(object sender, PopulatingEventArgs e)
{
string strFilePath = "";
string strNewFile = "";
strFilePath += #"../../FIXDictionaries/";
string typedString = txtCustomTagName.Text; ;
strNewFile = strFilePath + cmbFIXVerDataDictionary.Text + extension;
XDocument xmldoc = XDocument.Load(strNewFile);
List<string> tags = new List<string>();
IEnumerable<string> childNames = (from child in xmldoc.Element("fix").Element("fields").Descendants("field")
select child.Attribute("name").Value).Distinct().ToList();
foreach (string childName in childNames)
{
if (childName.StartsWith(typedString, StringComparison.InvariantCultureIgnoreCase))
{
tags.Add(childName);
}
}
txtCustomTagName.ItemsSource = tags;
}
}
How to do it?

I suppose the cause is you use ItemTemplate with empty TextBlock. Or don't use ItemTemplate at all or (in your case) replase it with <TextBlock Text="{Binding}" />

Related

How to validate RichTextbox in WPF?

How to validate RichTextbox in WPF? I want to validate the text for email and email separator i.e. emails should be entered with a semicolon.
Xaml:
<StackPanel Orientation="Horizontal">
<RichTextBox x:Name="txtEmail" Style="{StaticResource ContentRichTextBox}"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Auto" Margin="0,0,10,0">
<FlowDocument>
<Paragraph LineHeight="5"></Paragraph>
</FlowDocument >
</RichTextBox>
</StackPanel>
<StackPanel HorizontalAlignment="Center">
<TextBlock x:Name="txterrormessage" Width="300" Foreground="#FFE5572C" FontSize="14" Visibility="Hidden" TextWrapping="Wrap"></TextBlock>
</StackPanel>
<StackPanel HorizontalAlignment="Center" Margin="60,0,0,0">
<Button x:Name="BtnEmail" Style="{StaticResource ShortButtonStyle}" Content="NEXT" Margin="10" Command="{Binding CommandChanged}" CommandParameter="PROJECTS" Click="BtnEmail_Click"/>
</StackPanel>
This is my code:
private void BtnEmail_Click(object sender, RoutedEventArgs e)
{
string richText = new TextRange(txtEmail.Document.ContentStart, txtEmail.Document.ContentEnd).Text;
if (!Regex.IsMatch(richText, #"^[a-zA-Z][\w\.-]*[a-zA-Z0-9]#[a-zA-Z0-9][\w\.-]*[a-zA-Z0-9]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$"))
{
txterrormessage.Text = "Enter a valid email";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
else
{
txterrormessage.Visibility = System.Windows.Visibility.Hidden;
}
if (!Regex.IsMatch(richText, #"^((\w+([-+.]\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*)\s*[;]{0,1}\s*)+$"))
{
txterrormessage.Text = "Separate emails with ;";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
}
The code doesnt seem to work....How to validate?
Thanks
The simplest way I've seen to do this is
private void OnVerifyEmail()
{
var recipients = richText.Split(';', StringSplitOptions.RemoveEmptyEntries);
var validator = new System.ComponentModel.DataAnnotations.EmailAddressAttribute();
foreach (var recipient in recipients)
{
var isValid = validator.IsValid(recipient.Trim());
if(!isValid)
{
// do your thing here
}
}
}
format your richText before validation:
richText = Regex.Replace(richText, #"(\n|\r)", "", RegexOptions.Multiline);
Edit:
This is the whole method and probably what you're looking for:
private void BtnEmail_Click(object sender, RoutedEventArgs e)
{
string richText = new TextRange(txtEmail.Document.ContentStart, txtEmail.Document.ContentEnd).Text;
richText = Regex.Replace(richText, #"(\n|\r)", "", RegexOptions.Multiline);
richText = Regex.Replace(richText, #"( ;|; )", ";", RegexOptions.Multiline);
txterrormessage.Visibility = System.Windows.Visibility.Hidden;
if (!Regex.IsMatch(richText, #"^[\W]*([\w+\-.%]+#[\w\-.]+\.[A-Za-z]{2,4}[\W]*,{1}[\W]*)*([\w+\-.%]+#[\w\-.]+\.[A-Za-z]{2,4})[\W]*$"))
{
string[] emails = Regex.Split(richText, ";", RegexOptions.Multiline);
foreach (string item in emails)
{
if (string.IsNullOrEmpty(item))
continue;
if (!Regex.IsMatch(item, #"^\w+([-+.']\w+)*#\w+([-.]\w+)*\.\w+([-.]\w+)*$"))
{
txterrormessage.Text = item + " is not a valid email address";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
break;
}
}
if (string.IsNullOrEmpty(txterrormessage.Text))
{
txterrormessage.Text = "Separate emails with ; ";
txterrormessage.Visibility = System.Windows.Visibility.Visible;
}
}
}

An ObservableCollection of StackPanel as ItemsSource of ComboBox in WPF

I am having problem binding ObservableCollection as ItemsSource to a combo box (this combobox is in a listview of having some rows).
I followed A collection of StackPanel as ItemsSource of ComboBox but I did not get any clues for resolving my problem.
Problem:
I was able to add items to a combobox which is at the top on the form.
I have created a listview containing 3 text blocks and 1 combobox.
I am successful in populating data for the text blocks in listview.
But the problem lies with Combobox. First time, it shows all the items for each row in ListView. Once I select item or click on the combobox again to see the items, my list disappears. Only one combobox in the listview row shows all items. Other comboboxes shows blank.
Also, I was trying to save the index of the selected item and show the selected panel next time. But I did not get how to bind the stackpanel with selecteditem and selecteditemvalue.
I tried many ways of directly binding the items to the combobox in listview. But nothing worked. Request someone to help me on this.
Details of the code is given below:
I have XAML like below:
<Grid>
<Grid Height="40">
<ComboBox x:Name="cbList" />
</Grid>
<Grid Margin="0,56,0,168"></Grid>
<ListView x:Name="lvFirst" Margin="0,195,0,12">
<ListView.View>
<GridView >
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Width="50" x:Uid="tbListView1" Text="{Binding FirstName}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Width="50" x:Uid="tbListView2" Text="{Binding LastName}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Width="50" x:Uid="tbListView1" Text="{Binding ID}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox Width="100" x:Uid="cbListView" ItemsSource="{Binding Path=SPForComboBox}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
In the code behind I have a Contact class as below:
public class Contact : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void Notify(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
private string _fn;
private string _ln;
public string FirstName
{
get
{ return _fn; }
set
{ _fn = value; Notify("FirstName"); }
}
public string LastName
{
get
{ return _ln; }
set
{ _ln = value; Notify("LastName"); }
}
private int _id;
public int ID
{
get { return _id; }
set { _id = value; Notify("ID"); }
}
public StackPanel sp;
public override string ToString()
{
return FirstName + " " + LastName;
}
private ObservableCollection<StackPanel> _sp;
public ObservableCollection<StackPanel> SPForComboBox
{
get { return _sp; }
set { _sp = value; Notify("SPForComboBox"); }
}
}
To populate cbList Items, I am repeatedly calling below function after Initialization() function:
private void AddColours(string name, byte hexcolor)
{
//Add this ShiftType to Combo box
SolidColorBrush rectangleBrush = new SolidColorBrush();
Color color = new Color();
color.A = hexcolor;
rectangleBrush.Color = color;
System.Windows.Controls.StackPanel stkPanel = new StackPanel(); //stack panel to hold rectangle + text
stkPanel.Orientation = Orientation.Horizontal;
cbList.Items.Add(stkPanel);
Rectangle colorRect = new Rectangle(); //rectangle showing colour for shift
colorRect.Height = 12;
colorRect.Width = colorRect.Height;
colorRect.Fill = rectangleBrush;
stkPanel.Children.Add(colorRect);
System.Windows.Controls.TextBlock cboText = new TextBlock(); //Name of shift
cboText.Text = name;
cboText.Margin = new Thickness(5, 5, 5, 5);
stkPanel.Children.Add(cboText);
}
In the main window class, I have a created an ObservableCollection object as public static (object name is contacts).
public static ObservableCollection<Contact> contacts = new ObservableCollection<Contact>();
After the Initialization() function, I am populating contacts as below:
AddColours("First", 100);
AddColours("Second", 50);
AddColours("Third", 20);
AddColours("Fourth", 0);
AddColours("Fifth", 80);
Contact c1 = new Contact();
c1.FirstName = "Digo";
c1.LastName = "Maradona";
c1.ID = 0;
c1.SPForComboBox = new ObservableCollection<StackPanel>();
foreach (StackPanel sp in cbList.Items)
{
c1.SPForComboBox.Add(sp);
}
Contact c2 = new Contact();
c2.FirstName = "Brian";
c2.LastName = "Lara";
c2.ID = 1;
c2.SPForComboBox = new ObservableCollection<StackPanel>();
foreach (StackPanel sp in cbList.Items)
{
c2.SPForComboBox.Add(sp);
}
Contact c3 = new Contact();
c3.FirstName = "Sachin";
c3.LastName = "Tendulkar";
c3.ID = 2;
c3.SPForComboBox = new ObservableCollection<StackPanel>();
foreach (StackPanel sp in cbList.Items)
{
c3.SPForComboBox.Add(sp);
}
contacts.Add(c1);
contacts.Add(c2);
contacts.Add(c3);
lvFirst.ItemsSource = contacts;
HighCore, Thank you very much for the links. I have existing implementation and adding combobox to that.
I too felt that the method followed is not good. I shall certainly look at the alternatives provided by you and suggested by pushpraj.
Answer:
I thought referring objects in other combobox will work till the items exist in that combobox. But I need to create rectangle and textblock for reach combobox I am creating and for each entry in that combobox.
So certainly I need to do it in foreach loop.
Also, once I do this I can use SelectedIndex referring to the integer and SelectedItem to sp (individual stackpanel in that class).
This method is not good to follow but might be helpful for somebody.
Thanks.

C# WPF ObservableCollection does not update Grid

My implementation of an ObservableCollection does not update my Grid and I don't know why:
The first XAML-Window 'UserControlStaff.xaml' displays the Grid where the user data can be seen. The second XAML-Window allows to add users and is called 'CreateUser.xaml'.
The code behind the XAML-Windows is attached to this post as well.
My Code goes as follows:
[XAML] UserControlStaff.xaml
<UserControl x:Class="MyApp.UserControlStaff"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="786">
<StackPanel x:Name="spStaff" Orientation="Vertical" Visibility="Visible">
<Button Background="Azure" Click="btnCreateUser_Click" HorizontalAlignment="Left" Width="786" Height="40">
<Bold>Mitarbeiter hinzufügen</Bold></Button>
<ScrollViewer MaxHeight="504">
<DataGrid Background="#DBDB72" ItemsSource="{Binding UserDataObject}" CanUserAddRows="false" Width="786" />
</ScrollViewer>
</StackPanel>
</UserControl>
[Code] UserControlStaff.xaml.cs
namespace MyApp
{
/// <summary>
/// Interaktionslogik für UserControlStaff.xaml
/// </summary>
public partial class UserControlStaff : UserControl
{
ObservableCollection<User> mUserDataObject = new ObservableCollection<User>();
public ObservableCollection<User> UserDataObject
{
get
{
return mUserDataObject;
}
}
public UserControlStaff()
{
InitializeComponent();
DataContext = this;
try
{
string connectionString = ConfigurationManager.ConnectionStrings["MyApp.Properties.Settings.ConString"].ConnectionString;
string queryString = string.Empty;
using (SqlConnection connection = new SqlConnection(connectionString))
{
queryString = "SELECT ID, username AS Username, Password AS Passwort, (SELECT role FROM Roles WHERE ID = t1.role) AS Rolle FROM Users t1 ORDER BY ID";
SqlCommand cmd = new SqlCommand(queryString, connection);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dt = new DataTable("Users");
sda.Fill(dt);
foreach (DataRow row in dt.Rows)
{
int pID = 0;
string pUsername = "";
string pPassword = "";
string pRole = "";
foreach (DataColumn col in dt.Columns)
{
if (col.ToString().Trim() == "ID")
{
pID = int.Parse(row[col].ToString());
}
else if (col.ToString().Trim() == "Username")
{
pUsername = row[col].ToString();
}
else if (col.ToString().Trim() == "Passwort")
{
pPassword = row[col].ToString();
}
else if (col.ToString().Trim() == "Rolle")
{
pRole = row[col].ToString();
}
}
// Show Users DB table in MainWindow
mUserDataObject.Add(new User { ID = pID, Username = pUsername, Password = pPassword, Role = pRole });
}
// Show Users DB table in MainWindow
//XAML Grid: Name="gridUsers"
//CS Code: gridUsers.DataContext = dt.DefaultView;
}
}
catch {
throw;
}
}
private void btnCreateUser_Click(object sender, RoutedEventArgs e)
{
CreateUser popup = new CreateUser();
popup.Show();
}
private void UpdateUserData()
{
// implement
}
private void DeleteUserData()
{
// implement
}
}
}
[XAML] CreateUser.xaml
<Window x:Class="MyApp.CreateUser"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Mitarbeiter hinzufügen" Height="300" Width="400">
<Grid>
<Label Content="Username" HorizontalAlignment="Left" Margin="10,44,0,0" VerticalAlignment="Top" Width="120" Height="30"/>
<Label Content="Passwort" HorizontalAlignment="Left" Margin="10,88,0,0" VerticalAlignment="Top" Width="120" Height="30"/>
<Label Content="Rolle" HorizontalAlignment="Left" Margin="10,132,0,0" VerticalAlignment="Top" Width="120" Height="30"/>
<TextBox HorizontalAlignment="Left" Height="30" Margin="150,44,0,0" TextWrapping="Wrap" Name="TextBoxUsername" Text="{Binding Path=Username}" VerticalAlignment="Top" Width="180"/>
<TextBox HorizontalAlignment="Left" Height="30" Margin="150,88,0,0" TextWrapping="Wrap" Name="TextBoxPassword" Text="{Binding Path=Password}" VerticalAlignment="Top" Width="180"/>
<ComboBox HorizontalAlignment="Left" Height="30" Margin="150,132,0,0" VerticalAlignment="Top" Width="180" Loaded="ComboBox_Loaded" SelectionChanged="ComboBox_SelectionChanged"/>
<Button Click="btnSaveUserData_Click" Content="Speichern" HorizontalAlignment="Left" Margin="217,219,0,0" VerticalAlignment="Top" Height="30" Width="75"/>
<Button Click="btnCloseWindow_Click" Content="Abbrechen" HorizontalAlignment="Left" Margin="297,219,0,0" VerticalAlignment="Top" Height="30" Width="75"/>
</Grid>
</Window>
[Code] CreateUser.xaml.cs
namespace MyApp
{
/// <summary>
/// Interaktionslogik für CreateUser.xaml
/// </summary>
public partial class CreateUser : Window
{
User userObject;
public CreateUser()
{
InitializeComponent();
this.userObject = new User();
}
private void ComboBox_Loaded(object sender, RoutedEventArgs e)
{
// ToDO: Rollenzuweisung automatisieren aus Datenbanktabelle MyApp.Roles
List<string> data = new List<string>();
data.Add("Chef");
data.Add("Restaurantmitarbeiter");
data.Add("Fahrer");
// ... Get the ComboBox reference.
var comboBox = sender as ComboBox;
// ... Assign the ItemsSource to the List.
comboBox.ItemsSource = data;
// ... Make the second item selected.
comboBox.SelectedIndex = 1;
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// ... Get the ComboBox.
var comboBox = sender as ComboBox;
// ... Set SelectedItem as Window Title.
string value = comboBox.SelectedItem as string;
//this.Title = "Selected: " + value;
this.userObject.Role = value;
}
private void btnSaveUserData_Click(object sender, RoutedEventArgs e)
{
try
{
this.userObject.Username = TextBoxUsername.Text;
this.userObject.Password = TextBoxPassword.Text;
int UserRole;
// ToDO: Rollenzuweisung automatisieren aus Datenbanktabelle MyApp.Roles
if (this.userObject.Role == "Chef")
{
UserRole = 1;
}
else if (this.userObject.Role == "Restaurantmitarbeiter")
{
UserRole = 2;
}
else if (this.userObject.Role == "Fahrer")
{
UserRole = 3;
}
else
{
UserRole = 2; // Default UserRole is "Restaurantmitarbeiter"
}
if (this.userObject.Username.Trim() != "" && this.userObject.Password.Trim() != "")
{
CreateUserData(this.userObject.Username, this.userObject.Password, UserRole);
// ToDO: Update DataGrid in UserControlStaff
this.Close();
MessageBox.Show("Mitarbeiter hinzugefügt!");
} else {
MessageBox.Show("Bitte Username und Passwort eingeben.");
}
}
catch { throw; }
}
private void CreateUserData(string pUsername, string pPassword, int pRole)
{
string connectionString = ConfigurationManager.ConnectionStrings["MyApp.Properties.Settings.ConString"].ConnectionString;
string queryString = "INSERT INTO Users (ID, username, password, role) VALUES ((SELECT TOP 1 ID+1 FROM Users ORDER BY ID DESC), '" + pUsername + "', '" + pPassword + "', '" + pRole + "')";
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand(queryString, connection);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dt = new DataTable("Users");
sda.Fill(dt);
string UserRole;
if (pRole == 1)
{
UserRole = "Chef";
}
else if (pRole == 2)
{
UserRole = "Restaurantmitarbeiter";
}
else if (pRole == 3)
{
UserRole = "Fahrer";
}
else
{
UserRole = "Restaurantmitarbeiter"; // Default UserRole is "Restaurantmitarbeiter"
}
// Add to Observable Collection
// ToDO
// mUserDataObject.Add(new User { ID = pID, Username = pUsername, Password = pPassword, Role = pRole });
}
private void btnCloseWindow_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
}

Get Selected Item from Context Menu

I am creating a set of images dynamically and putting them into a Stack Panel like this :-
Image image = new Image();
image.Name = "image_" + iCounter;
image.Height = 100;
image.Width = 100;
image.Source = bitmap;
image.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
image.Stretch = Stretch.Fill;
image.VerticalAlignment = VerticalAlignment.Top;
//image.MouseDown += new MouseButtonEventHandler(image_MouseDown);
image.ToolTip = "Right-Click for Options";
image.ContextMenu = GetContextMenu();
Separator separator = new Separator();
separator.Name = "separator_" + iCounter;
AddImagesStackPanel.Children.Add(image);
AddImagesStackPanel.Children.Add(separator);
iCounter++;
Then in the Context Menu I have this code :-
private System.Windows.Controls.ContextMenu GetContextMenu()
{
System.Windows.Controls.MenuItem mi1;
System.Windows.Controls.MenuItem mi2;
System.Windows.Controls.ContextMenu _contextMenu = new System.Windows.Controls.ContextMenu();
mi1 = new System.Windows.Controls.MenuItem();
mi1.Header = "Show Normal Size";
mi1.Click += new RoutedEventHandler(ContextMenuItem1_Click);
mi2 = new System.Windows.Controls.MenuItem();
mi2.Header = "Remove image";
mi2.Click += new RoutedEventHandler(ContextMenuItem2_Click);
_contextMenu.Items.Add(mi1);
_contextMenu.Items.Add(mi2);
return _contextMenu;
}
Now I wish to get the selected item when the user right clicks on an image and I have this code :-
private void ContextMenuItem2_Click(object sender, RoutedEventArgs e)
{
object obj = e.OriginalSource;
string imageName = ((System.Windows.Controls.Image)obj).Name;
string[] split = imageName.Split('_');
imageUploads.RemoveAt(Convert.ToInt32(split[1]));
DisplayImagesInStackPanel(imageUploads);
}
But obj does not contain the name of the image since its a RoutedEventArgs. Is there any way I can get the selected item in the context menu?
After discussing this in the comments this should work:
// The binding source.
private readonly ObservableCollection<BitmapImage> _imageList = new ObservableCollection<BitmapImage>();
public ObservableCollection<BitmapImage> ImageList
{
get { return _imageList; }
}
How to display this and set up the ContextMenu:
<ItemsControl ItemsSource="{Binding ImageList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding}" Width="100" Height="100"
HorizontalAlignment="Left" Stretch="Fill"
VerticalAlignment="Top" ToolTip="Right-Click for Options">
<Image.ContextMenu>
<ContextMenu>
<MenuItem Header="Show Normal Size" Click="Image_CM_ShowNormalSize_Click"
Tag="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget}"/> <!-- The placement target is the object to which the context menu belongs, i.e. the image -->
<MenuItem Header="Remove Image" Click="Image_CM_RemoveImage_Click"
Tag="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.DataContext}"/> <!-- The DataContext of the Image is the BitmapImage, which should be removed from the list -->
</ContextMenu>
</Image.ContextMenu>
</Image>
<Separator/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
What the handlers might look like:
private void Image_CM_ShowNormalSize_Click(object sender, RoutedEventArgs e)
{
Image img = (sender as FrameworkElement).Tag as Image;
img.Width = (img.Source as BitmapImage).PixelWidth;
img.Height = (img.Source as BitmapImage).PixelHeight;
}
private void Image_CM_RemoveImage_Click(object sender, RoutedEventArgs e)
{
BitmapImage img = (sender as FrameworkElement).Tag as BitmapImage;
// If the image is removed from the bound list the respective visual elements
// will automatically be removed as well.
ImageList.Remove(img);
}
But obj does not contain the name of the image since its a RoutedEventArgs.
True, but the obj at that point is a MenuItem, if you drill one level down, you can get the image.
Is there any way I can get the selected item in the context menu?
Normally one would load the model classes (Image in your case) through the binding of ItemSource of the Menu (or MenuItem if they are to be submenus) and if one takes that route they can pull the originating item off of the DataContext such as in my case the item was an MRU class item.
private void SelectMRU(object sender, RoutedEventArgs e)
{
var mru = (e.OriginalSource as MenuItem).DataContext as MRU;
var name = mru.Name;
...
}
Because you do things by hand you should load the Tag property of the MenuItem with the Image in question
mi1.Tag = {Image instance in question};
then extract on the event.
var image = (e.OriginalSource as MenuItem).Tag as Image;

How can I bind Wpf DataGridColumn to an object?

I want to bind the columns of my WPF DataGrid to some objects in a Dictionary like this:
Binding Path=Objects[i]
where Objects is my Dictionary of objects, so that each cell will represent an Object element. How can I do that?
I suppose that I need to create a template for my cell, which I did, but how to get the result of column binding in my template? I know that by default the content of a DataGridCell is a TextBlock and it's Text property is set through column binding result, but if that result is an object I guess that I have to create a ContentTemplate. How do I do that, as the stuff I tried is not displaying anything.
Here it is what I tried:
<Style x:Key="CellStyle" TargetType="{x:Type dg:DataGridCell}">
<Setter Property="Template"> ---it should realy be ContentTemplate?
<Setter.Value>
<ControlTemplate>
<controls:DataGridCellControl CurrentObject="{Binding }"/> -- I would expect to get the object like this for this column path : Path=Objects[i] but is not working
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
So, to make myself completly clear, i want to get in CurrentObject property of my DataGridCellControl the current object that should result if I set the column binding in my data grid like this Path=Objects[i].
Thank you for any suggestion,
John.
Try this:
<ListView x:Name="listViewUsers" SelectionMode="Single"
ItemsSource="{Binding ElementName=window1, Path=Users, Mode=TwoWay}" MouseDoubleClick="listViewUsers_MouseDoubleClick">
<ListView.View>
<GridView x:Name="gridViewUsers" AllowsColumnReorder="False">
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Path=IsAdministrator, Converter={StaticResource boolToImage}, ConverterParameter='Images/admin18.gif|Images/user18.gif'}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="User Name" DisplayMemberBinding="{Binding Path=UserName}" Width="140" />
<GridViewColumn Header="Full Name" DisplayMemberBinding="{Binding Path=FullName}" Width="140" />
<GridViewColumn Header="Phone Number" DisplayMemberBinding="{Binding Path=PhoneNumber}" Width="110" />
<GridViewColumn Header="Access Type" DisplayMemberBinding="{Binding Path=AccessType}" Width="110">
</GridViewColumn>
<GridViewColumn>
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Cursor="Hand" ToolTip="Delete User" Stretch="None" Source="Images/trash12.gif" MouseUp="DeleteUser" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Where in ItemsSource="{Binding ElementName=window1, Path=Users, Mode=TwoWay}"
ElementName is the name of the Window in XAML (just add x:Name="window1" to the Window tag as with any other ontrol.
Users is a List, should work the same with Dictionary
Mode=TwoWay means that if the grid gets modified, the list will get modified too, and vice versa (Two way binding)
EDIT:
Try this:
XAML:
<ListView x:Name="listViewTest" ItemsSource="{Binding}">
<ListView.View>
<GridView x:Name="gridViewTest">
</GridView>
</ListView.View>
</ListView>
C#:
public class TheClass
{
public int Col1, Col2, Col3;
public Dictionary<int, OtherColumns> otherColumns = new Dictionary<int,OtherColumns>();
}
public class OtherColumns
{
public string ColumnName;
public int Value;
}
And call this method under Window_Loaded:
private void PopulateListView()
{
TheClass c = new TheClass();
c.Col1 = 10;
c.Col2 = 20;
c.Col3 = 30;
c.otherColumns.Add(0, new OtherColumns() { ColumnName = "Col4", Value = 40 });
c.otherColumns.Add(1, new OtherColumns() { ColumnName = "Col5", Value = 50 });
c.otherColumns.Add(3, new OtherColumns() { ColumnName = "Col6", Value = 60 });
DataTable table = new DataTable();
// adding regular columns
table.Columns.Add("Col1", typeof(int));
table.Columns.Add("Col2", typeof(int));
table.Columns.Add("Col3", typeof(int));
// adding dynamic columns
foreach (KeyValuePair<int, OtherColumns> pair in c.otherColumns)
{
table.Columns.Add(pair.Value.ColumnName, typeof(int));
}
DataRow row = table.NewRow();
// adding regular column values to the DataTable
row["Col1"] = c.Col1;
row["Col2"] = c.Col2;
row["Col3"] = c.Col3;
// adding dynamic column values to the DataTable
foreach (KeyValuePair<int, OtherColumns> pair in c.otherColumns)
{
row[pair.Value.ColumnName] = pair.Value.Value;
}
table.Rows.Add(row);
// Start binding the table.
gridViewTest.Columns.Clear();
System.Windows.Controls.GridViewColumn gvc;
Binding binding;
foreach (DataColumn column in table.Columns)
{
gvc = new System.Windows.Controls.GridViewColumn();
binding = new System.Windows.Data.Binding();
binding.Path = new PropertyPath(column.ColumnName);
binding.Mode = BindingMode.OneWay;
gvc.Header = column.Caption;
gvc.DisplayMemberBinding = binding;
gridViewTest.Columns.Add(gvc);
}
listViewTest.DataContext = table;
}
I'm not saying it's the best solution, but it could help. Let me know.
I made some helper classes so I could use the DataGrid as a kind of DataTable. In other words, I wanted the formatting, sorting, and polished look of the DataGrid without having to pre-fab some classes beforehand. The main reason I wanted this was for a testing suite, I wanted to be able to create an arbitrary number of columns and at runtime. Here's what I got
public class DataRow
{
internal List<object> Items = new List<object>();
public object this[string value]
{
get { return Items[Convert.ToInt32(value)]; }
}
public string GetString(int index)
{
return Items[index].ToString();
}
public object GetObject(int index)
{
return Items[index];
}
public DataRow(params object[] values)
{
if (values == null || values.Length < 1)
throw new Exception("You must pass in some values");
Items.AddRange(values);
}
}
public class GridConstructor
{
public List<DataRow> Rows = new List<DataRow>();
private DataRow headers;
public GridConstructor(DataRow head)
{
headers = head;
}
public void BuildInto(DataGrid grid)
{
grid.AutoGenerateColumns = false;
grid.Columns.Clear();
int totalCols = 0;
Type headType = headers.GetType();
for (int i = 0; i < headers.Items.Count; i++)
{
grid.Columns.Add(GetCol(headers.GetString(i), String.Concat("[", i.ToString(),"]")));
totalCols++;
}
int finalWidth = totalCols * (int)grid.ColumnWidth.Value + 15;
grid.Width = finalWidth;
grid.ItemsSource = Rows;
}
private DataGridTextColumn GetCol(string header, string binding)
{
DataGridTextColumn col = new DataGridTextColumn();
col.IsReadOnly = true;
col.Header = header;
col.Binding = new Binding(binding);
return col;
}
public DataGrid Create(int colSize)
{
DataGrid grid = new DataGrid();
grid.ColumnWidth = colSize;
grid.CanUserAddRows = false;
grid.AlternationCount = 2;
BuildInto(grid);
return grid;
}
}
Putting this together, this is a sample use:
void SimpleTest_Loaded(object sender, RoutedEventArgs e)
{
DataRow headers = new DataRow("Level", "Weapon Type", "vs None", "vs Leather", "vs Studded", "vs Brigandine");
GridConstructor gridConstructor = new GridConstructor(headers);
var weaponType = "Slash";
for (int level = 1; level < 10; level++)
{
int damage = DiceCup.RollMulti(8, level);
int damCloth = damage - DiceCup.RollMulti(2, level);
int damLeather = damage - DiceCup.RollMulti(3, level);
int damStudded = damage - DiceCup.RollMulti(4, level);
int damBrigandine = damage - DiceCup.RollMulti(5, level);
DataRow row = new DataRow(level, weaponType, damage, damCloth, damLeather, damStudded, damBrigandine);
gridConstructor.Rows.Add(row);
}
//Create the grid.
var grid = gridConstructor.Create(100);
//Create a chart.
Chart chart = new Chart();
chart.Height = 200;
chart.LegendTitle = "Legend";
chart.Title = "Slash vs Armor Types";
chart.DataContext = gridConstructor.Rows;
//Create our series, or lines.
LineSeries slashVsNone = new LineSeries();
slashVsNone.Title = "vs None";
slashVsNone.DependentValueBinding = new Binding("[2]");
slashVsNone.IndependentValueBinding = new Binding("[0]");
slashVsNone.ItemsSource = gridConstructor.Rows;
chart.Series.Add(slashVsNone);
//Presentation is a stackpanel on the page.
presentation.Children.Add(grid);
presentation.Children.Add(chart);
}
And the output:
alt text http://quiteabnormal.com/images/codeSample.jpg
Please note that the grid coloring is from universal styles set on the page. If you use the GridConstructor.BuildInto() method you can specify a grid you've pre-formatted yourself in Blend or somesuch.
Just one thing, the GridConstructor makes some assumptions about the column's initial settings. You can change the class to make it more customizable if you like, but this is what I needed so I wanted to be able to make it without fuss.

Resources