I am trying to convert my Winforms application to WPF format, I am trying to convert the double click of my listview at the moment. I have done this so far:
private void listView1_DoubleClick(object sender, MouseButtonEventArgs e)
{
ListViewItem item = this.listView1.SelectedItems[0];
if (item.Tag != null)
{
ControllerInfo controllerInfo = (ControllerInfo)item.Tag;
if (controllerInfo.Availability == Availability.Available)
{
if (controllerInfo.IsVirtual)
{
this.controller = ControllerFactory.CreateFrom(controllerInfo);
this.controller.Logon(UserInfo.DefaultUser);
listView1.Items.Clear();
listView1.Items.Add(item);
EnableControllerFunctionality();
}
else //real controller
{
if (MessageBox.Show("This is NOT a virtual controller, do you really want to connect to that?","Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
{
this.controller = ControllerFactory.CreateFrom(controllerInfo);
this.controller.Logon(UserInfo.DefaultUser);
listView1.Items.Clear();
listView1.Items.Add(item);
EnableControllerFunctionality();
}
}
}
else
{
MessageBox.Show("Selected controller not available.");
}
}
The problem is with the first line :
ListViewItem item = this.listView1.SelectedItems[0];
stating 'cannot implicitly convert from object to listviewitem. An explicit conversion exists
I have been trying to find this conversion but to no avail. I feel as though I am missing something key.
Related
I want to search in a data grid via typing in a textbox, but I am unable to find solution.
Do I need to do any binding? If so, then how do I do it?
If you want filter text in your Datagrid i.e by Name, try this...
private bool DataMatchesFilterText(User user, string filterText)
{
return user.Name.ToString() == filterText;
}
Yeah you will require your data grid to be bound to a Property that contains all your data.
Then add a event handler to your Textbox to act on one of the key events, e.g.
Xaml:
<TextBox x:Name="SearchBox" KeyUp="FilterTextBox_TextChanged" />
Then in the code behind you need to act on that event. Here you need to extract the filter text, get the rows in your DataGrid and then perform some method to determine if it should be visible or not. You will need to implement your own DataMatchesFilterText method.
Codebehind:
private void FilterTextBox_TextChanged(object sender, KeyEventArgs e)
{
var filterTextBox = (TextBox)sender;
var filterText = filterTextBox.Text;
SetRowVisibilityByFilterText(filterText);
}
private void SetRowVisibilityByFilterText(string filterText)
{
GetVisibleRows(yourGrid)
.ToList()
.ForEach(
x =>
{
if (x == null) return;
x.Visibility =
DataMatchesFilterText(x.Item as YourRowProperty, filterText) ? Visibility.Visible : Visibility.Collapsed;
});
}
public static IEnumerable<DataGridRow> GetVisibleRows(DataGrid grid)
{
if (grid == null || grid.Items == null) yield break;
int count = grid.ItemsSource == null
? grid.Items.Count
: grid.ItemsSource.Cast<object>().Count();
for (int i = 0; i < count; i++)
{
yield return (DataGridRow)grid.ItemContainerGenerator.ContainerFromIndex(i);
}
}
I replaced the DatePickerTextBox in the control template of DatePicker, and it no longer ensures a valid date value for the text entered. I tested different strings with a plain DatePickerTextBox, too, and it didn't validate them, so it must be some kind of interaction between DatePickerTextBox and DatePicker. Is there a way to get that interaction with a different TextBox control in the control template that isn't DatePickerTextBox, or will I have to recreate that validation in my own custom control?
Short explanation with answer:
In the OnApplyTemplate method of a custom TextBox control, I added:
public override void OnApplyTemplate()
{
LostFocus += OnLostFocus;
base.OnApplyTemplate();
}
And then the method:
private void OnLostFocus(object sender, RoutedEventArgs e)
{
if (!Text.Equals(oldText))
{
DateTime d;
if (!DateTime.TryParse(Text, out d))
{
Text = oldText;
}
}
oldText = Text;
}
Long explanation of why it doesn't work in the control template if you replace DatePickerTextBox:
I poked around in the reference source and discovered the following:
In the OnApplyTemplate method for DatePicker, some handlers are added, specifically for a DatePickerTextBox, which it will not apply if it doesn't find one (which explains why DatePickerTextBox doesn't validate on its own, either - DatePicker adds the handlers, not DatePickerTextBox):
_textBox = GetTemplateChild(ElementTextBox) as DatePickerTextBox;
if (this.SelectedDate == null)
{
SetWaterMarkText();
}
if (_textBox != null)
{
_textBox.AddHandler(TextBox.KeyDownEvent, new KeyEventHandler(TextBox_KeyDown), true);
_textBox.AddHandler(TextBox.TextChangedEvent, new TextChangedEventHandler(TextBox_TextChanged), true);
_textBox.AddHandler(TextBox.LostFocusEvent, new RoutedEventHandler(TextBox_LostFocus), true);
if (this.SelectedDate == null)
{
if (!string.IsNullOrEmpty(this._defaultText))
{
_textBox.Text = this._defaultText;
SetSelectedDate();
}
}
else
{
_textBox.Text = this.DateTimeToString((DateTime)this.SelectedDate);
}
}
So, tracking it down(here comes the fun part)...
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
SetSelectedDate();
}
Then:
private void SetSelectedDate()
{
if (this._textBox != null)
{
if (!string.IsNullOrEmpty(this._textBox.Text))
{
string s = this._textBox.Text;
if (this.SelectedDate != null)
{
// If the string value of the SelectedDate and the TextBox string value are equal,
// we do not parse the string again
// if we do an extra parse, we lose data in M/d/yy format
// ex: SelectedDate = DateTime(1008,12,19) but when "12/19/08" is parsed it is interpreted as DateTime(2008,12,19)
string selectedDate = DateTimeToString(this.SelectedDate.Value);
if (string.Compare(selectedDate, s, StringComparison.Ordinal) == 0)
{
return;
}
}
DateTime? d = SetTextBoxValue(s);
if (!this.SelectedDate.Equals(d))
{
this.SetCurrentValueInternal(SelectedDateProperty, d);
this.SetCurrentValueInternal(DisplayDateProperty, d);
}
}
else
{
if (this.SelectedDate.HasValue)
{
this.SetCurrentValueInternal(SelectedDateProperty, (DateTime?)null);
}
}
}
else
{
DateTime? d = SetTextBoxValue(_defaultText);
if (!this.SelectedDate.Equals(d))
{
this.SetCurrentValueInternal(SelectedDateProperty, d);
}
}
}
The important thing there is the call to SetTextBoxValue:
private DateTime? SetTextBoxValue(string s)
{
if (string.IsNullOrEmpty(s))
{
SafeSetText(s);
return this.SelectedDate;
}
else
{
DateTime? d = ParseText(s);
if (d != null)
{
SafeSetText(this.DateTimeToString((DateTime)d));
return d;
}
else
{
// If parse error:
// TextBox should have the latest valid selecteddate value:
if (this.SelectedDate != null)
{
string newtext = this.DateTimeToString((DateTime)this.SelectedDate);
SafeSetText(newtext);
return this.SelectedDate;
}
else
{
SetWaterMarkText();
return null;
}
}
}
}
And the important thing there is the call to ParseText:
private DateTime? ParseText(string text)
{
DateTime newSelectedDate;
// TryParse is not used in order to be able to pass the exception to the TextParseError event
try
{
newSelectedDate = DateTime.Parse(text, DateTimeHelper.GetDateFormat(DateTimeHelper.GetCulture(this)));
if (Calendar.IsValidDateSelection(this._calendar, newSelectedDate))
{
return newSelectedDate;
}
else
{
DatePickerDateValidationErrorEventArgs dateValidationError = new DatePickerDateValidationErrorEventArgs(new ArgumentOutOfRangeException("text", SR.Get(SRID.Calendar_OnSelectedDateChanged_InvalidValue)), text);
OnDateValidationError(dateValidationError);
if (dateValidationError.ThrowException)
{
throw dateValidationError.Exception;
}
}
}
catch (FormatException ex)
{
DatePickerDateValidationErrorEventArgs textParseError = new DatePickerDateValidationErrorEventArgs(ex, text);
OnDateValidationError(textParseError);
if (textParseError.ThrowException && textParseError.Exception != null)
{
throw textParseError.Exception;
}
}
return null;
}
The last two methods have the validation I was looking for. If ParseText returns null, then SetTextBoxValue reverts the value in the text box back to its previous value. Because DatePicker is the control that adds the handler, you won't get the validation outside of DatePicker. Since DatePicker looks specifically for a DatePickerTextBox named "PART_TextBox" (that is what the ElementTextBox string is), a DatePickerTextBox must be used, or the handler will not be applied (since _textbox will be null).
public void MouseSingleClickEditable(object sender, MouseButtonEventArgs e)
{
if ((!datagrid.HasItems) || datagrid.SelectedIndex < 0) return;
DataGridColumn clmn = datagrid.CurrentColumn;
if (clmn != null)
{
String columnType = clmn.GetType().Name;
switch (columnType)
{
case "DataGridTemplateColumn":
case "DataGridCheckBoxColumn":
row = (DataGridRow)datagrid.ItemContainerGenerator.ContainerFromItem(datagrid.Items[datagrid.SelectedIndex]);
if (!row.IsEditing)
{
datagrid.IsReadOnly = false;
datagrid.BeginEdit();
}
break;
default:
break;
}
}
}
I have 2 DatePickers, Checkbox and a combobox in my WPF Datagrid but while geeting the column type I am getting it only as DataGridTemplateColumn instead of type DataGridDatePickerColumn or DataGridComboboxColumn. How to extract the exact type of Control from the DataGridTemplateColumn.
public void MouseSingleClickEditable(object sender, MouseButtonEventArgs e) {
var datagrid = new DataGrid();
if ((!datagrid.HasItems) || datagrid.SelectedIndex < 0) return;
DataGridColumn clmn = datagrid.CurrentColumn;
if (clmn != null) {
if (clmn is DataGridCheckBoxColumn) {
//do something
} else if (clmn is DataGridTemplateColumn) {
var templateColumn = (DataGridTemplateColumn)clmn;
var rootControlOfCellTemplate = templateColumn.CellTemplate.LoadContent();
var rootControlOfCellEditingTemplate = templateColumn.CellEditingTemplate.LoadContent();
// you can now check for types of the template. CellEditingTemplate is for template in edit mode, and CellTemplate for "non-edit" mode
// for example
if (rootControlOfCellTemplate is Button) {
//do something
}
if (rootControlOfCellEditingTemplate is DatePicker) {
//do something
}
}
}
}
Note how I check for types. Normally you should do it like this, not by hard-coded strings.
I have a problem regarding the comportment my ComboBox.
First I use a combobox to display all elements in a IEnumarale.
Then, with a button wich open a popup, the user can add an alement to that list.
The problem is that when the user validate his choice and close the popup, the element is not automatly added to the ComboBox without doing a refresh of the page.
The combobox is coded as follows :
<telerik:RadComboBox x:Name="MyElements"
SelectionChanged="MyElements_OnSelectionChanged"
ItemTemplate="{StaticResource ComboBoxElementsTemplate}"
ItemsSource="{Binding ListElements}"/>
The constructor of the list is :
public IEnumerable<Element> ListElements
{
get { return _listElements; }
set
{
_listElements= value;
RaisePropertyChange("ListElements");
}
}
And the code behind of the button to validate the user choice in the popup :
private ObservableCollection<HistoriqueElement> elementList = null;
private void SelectClick(object sender, RoutedEventArgs e)
{
var element= _GridList.SelectedItem as HistoriquePasserelle;
if (_GridList.SelectedItem != null)
{
var installation = this.DataContext as Installation;
if (installation != null && element!= null)
{
element.DateFin = DateTime.Now;
HistoriqueElement newElement= new HistoriqueElement()
{
Installation = installation,
ContactAction = GlobalActeurs.Current.CurrentContact,
Date = DateTime.Now,
Element = element.Element,
StatutElement = element.StatutElement ,
Owner= element.Owner,
};
elementList.Remove(element);
}
MainPage.ClosePopup();
}
}
When the user choose a new element in the list display in the popup and validate his choice, he returns to the main page, but his choice is not automatically added to the combobox.
I can post you any parts of the code.
Thank you in advance.
The method OnDataContextChanged :
public override void OnDataContextChanged(DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is Installation)
{
if (MainPage.CurrentInstallation.LastElements != null)
{
ListElements = MainPage.CurrentInstallation.LastElements;
MyElements.SelectedIndex = 0;
}
else
{
LoadOperation<Element> operation =
_context.Load(_context.GetCurrentElementsByInstallationId(MainPage.CurrentInstallation.Id));
this._busy.IsBusy = true;
operation.Completed += delegate
{
this._busy.IsBusy = false;
if (operation.ManageError())
{
ListElements = operation.Entities;
}
};
}
this.DataContext = this;
}
else
{
RaisePageTitleChanged();
if (MainPage.CurrentInstallation == null)
return;
}
if (MyElements.SelectedItem == null && MyElements.Items.Any())
{
MyElements.SelectedIndex = 0;
}
}
If the collection the ItemsSource is bound to implement INotifyCollection changed, that is, it's an ObservableCollection<>, then the combobox will be notified of any changes to the collection and you will not need to rebind or refresh, it will all be automatic.
Once you add the item to the list, bind the itemsource to the combobox, then you dont have to refersh.
MyElements.ItemsSource = ListElements
I have a form with several controls. There a situtions where 'textBoxOtherRelationship' is disable and the text is set to string.empty. But when I then got to another control and tab out the data appears again,while the control remains disabled.
textBoxOtherRelationship.DataBindings.Add(new Binding("Text", _binder, "RelationshipNotes"));
private void ComboBoxRelationShipSelectedValueChanged(object sender, EventArgs e)
{
if ((Constants.Relationship)comboBoxRelationShip.SelectedItem.DataValue == Constants.Relationship.Other)
{
textBoxOtherRelationship.Enabled = true;
if (_formMode != ActionMode.ReadOnly)
{
textBoxFirstName.BackColor = Color.White;
}
}
else
{
textBoxOtherRelationship.Enabled = false;
_model.RelationshipNotes = null;
textBoxOtherRelationship.Text = string.Empty;
if (_formMode != ActionMode.ReadOnly)
{
textBoxFirstName.BackColor = Color.LightYellow;
}
}
}
Hmm.. so I see this line here:
textBoxOtherRelationship.DataBindings.Add(
new Binding("Text", _binder, "RelationshipNotes"));
which tells me that you've got binding set up between the Text property on the textBoxOtherRelationship and a property called "RelationshipNotes" on the datasource _binder.
Great.
So, I'm assuming that the two-way binding works just fine and that when you type something into the textBoxOtherRelationship and that control loses focus the underlying RelationshipNotes property is getting updated as well, right?
Now, looking at your code there, I don't think the underlying datasource is being updated when you set the Text property to string.Empty because that usually doesn't happen until the textbox loses focus and you've disabled the control.
If you add:
textBoxOtherRelationship.DataBindings[0].WriteValue();
after you set the value to string.Empty that string.Empty value will get stored back to the datasource because the databinding will know there is something to update. Programmatically, it doesn't.
I see you have this line:
textBoxOtherRelationship.Enabled = false;
_model.RelationshipNotes = null; <<<----------------------
textBoxOtherRelationship.Text = string.Empty;
Is _model.RelationshipNotes what is ultimately supposed to be bound to that textbox?
The SelectedIndexChanged event doesn't commit the databindings until after the control loses focus, so the quick fix is to write the value first in your event:
private void ComboBoxRelationShipSelectedValueChanged(object sender, EventArgs e)
{
if (comboBoxRelationShip.DataBindings.Count > 0) {
comboBoxRelationShip.DataBindings[0].WriteValue();
if ((Constants.Relationship)comboBoxRelationShip.SelectedItem.DataValue ==
Constants.Relationship.Other) {
textBoxOtherRelationship.Enabled = true;
if (_formMode != ActionMode.ReadOnly) {
textBoxFirstName.BackColor = Color.White;
}
} else {
textBoxOtherRelationship.Enabled = false;
_model.RelationshipNotes = null;
textBoxOtherRelationship.Text = string.Empty;
if (_formMode != ActionMode.ReadOnly) {
textBoxFirstName.BackColor = Color.LightYellow;
}
}
}
}