connect object to listViewItem - c#

I have an overview of some objects, displayed in a listView.
When an object is selected I want to show a form containing more details about the selected item.
public lessonForm(lesson foo)
[get and display data]
[...]
lessonListView.ItemActivate += lessonSelected;
void lessonSelected(object sender, eventArgs e)
{
lesson ??? = //REQUESTION MAGIC here.
new lessonForm(???).Show();
}
Since ListViewItems are acutally just texts and not programmatically connected to the lesson-object I used to create them, I have not found a proper way to find the respective lesson-object for each listViewItem.
Sure I could do
lesson ??? = Program.listOfAllLessons.Find((candidate) => {
return candidate.plainTextName == selectedItem.Text //abbrev. on purpose
});
However I think it is undisputed that that is just horrible code, on more than one level.
Basically:
I would wish for listViewItem to have an
obj underlyingObject;
field that allows for easy access to the object represented by the listViewItem.
Is there a functionality that allows for this?

You could use the Tag property to store the associated object when creating the ListViewItem. As Tag is of type object you'd need to cast it appropriately when you read from it.

Related

WPF gets "old" value when data in ListView change

I have the TreeView with objects. When I select item of these tree, than other control - ListView displays as its items properties of selected object. I want to save values of properties when in TreeView selection is change to other object.
So, is there a good way in WPF to gets values of "just before changing" items in ListView control? My idea for now is to override the PreviewMouseDown to check if user click tree node. By god way I mean better than mine. Maybe something in ListView template?
Indication that there is no need to change my idea with the PreviewMouseDown will be also good answer.
Could you please provide the relevant code snippets? I try to answer your question, but I'm not sure I understood it correctly.
If you bind the SelectedItem of you TreeView to a property (a.e. using MVVM pattern), you can save the values before actually setting the item.
Doing so in the setter is not so good though, because it becomes quite large then. I would have a setter like this:
private Foo bar;
public Foo Bar
{
get { return bar; }
set
{
OnPropertyChanging("Bar");
bar=value;
OnPropertyChanged("Bar");
}
}
Then you can listen to your own PropertyChanging events and do your stuff there:
private void this_PropertyChanging(object param, PropertyChangingEventArgs e)
{
switch(e.PropertyName)
{
case "Bar":
//Do you stuff
break,
}
}

Multiple ComboBoxEdit controls with same items

I am working on a user control which contains a SpinEdit (numeric up-down) control and a ComboBoxEdit. The option selected in the combo-box provides a factor by which number in the SpinEdit is multiplied. At the moment, I have something like this:
public class MyUserControl : DevExpress.XtraEditors.XtraUserControl
{
private static List<String> listItems;
static MyUserControl() // Populate the list of options with the default options
{
listItems = new List<String?();
listItems.Add("Option1");
listItems.Add("Option2");
listItems.Add("Option3");
}
public MyUserControl()
{
InitializeComponent();
// Add default options to the combo box
foreach (String item in listItems)
{
this.cboBox.Properties.Items.Add(item);
}
}
}
That works fine (note that the above is simplified, in reality the static list is a static Dictionary which maps the strings to the multiplication factor) except that I want to allow the user to add custom options to listItems and have these appear on every instance of this user control in my application. That is why listItems is static, as my hope was to do this.cboBox.Properties.Items = listItems; so that any additions to listItems would appear on every control. However, the Items property is read-only so I cannot do that.
How can I go about ensuring every instance of my user control has the same set of options, even if these are changed? Having the static members fire an event when the user changes the option list might do the trick, but that seems a bit overkill for something that looks as simple as this. Does anyone have any other ideas?
In your case is better to use LookUpEdit control instead of ComboBoxEdit. Just make some adjustments to it:
lookUpEdit1.Properties.ShowHeader = false;
You can use your listItems in that way:
lookUpEdit1.Properties.DataSource = listItems;
But there is some problem with Dictionary as DataSource. For DataSource you must use an collection that implements IList, ITypedList or IBindingList interface. So, you can convert your Dictionary to List or you can use this trick:
private static Dictionary<int, string> items;
//...
lookUpEdit1.QueryPopUp += lookUpEdit_QueryPopUp;
lookUpEdit2.QueryPopUp += lookUpEdit_QueryPopUp;
//...
private void lookUpEdit_QueryPopUp(object sender, CancelEventArgs e)
{
var lookUpEdit = (LookUpEdit)sender;
lookUpEdit.Properties.DataSource = null;
lookUpEdit.Properties.DataSource = items;
}
But I think is better to use List instead of Dictionary.
I don't know about the DevExpress controls (you might want to ask on their support forums), but in classic Windows comboboxes, the list is maintained inside the control. The Items property is a .Net wrapper that makes the internal structure easier to work with. So, it's not possible to assign the same data structure to each combobox. They have their own copy.
Your idea to have a publish/subscribe mechanism to what are now static lists seems like a reasonable solution.
If it were me, instead of having static stuff on the control class, I'd make that master list container a separate class and have a property on the control that takes an instance of that class. It'd make your overall architecture a bit more flexible and testable.

rename control in wpf using c#

if I add control in Microsoft Blend 4 without set Name to this control and I want to set name to it and use it in c# how ?
example I added button using Blend in my layout but without give it a name
I want to give it a name using c# without x:Name="" in xaml
In your place I would give LogicalTreeHelper.GetChildren (this) a chance. It returns a collection of children to Window (this is a handle to Window) Reference MSDN
From there you can try to find your control.
But I think it is easier to try to rewrite the control (or look for another component) so you can have names on the children. That was your problem from the start.
Hope it helps
Gorgen
First, why in the world would you want to do that?
If you do not set a name you have no easy way of accessing the control. However you can get access to the control via relationships to other controls or events that pass a reference, for example the loaded event.
e.g.
private void Menu_Loaded(object sender, RoutedEventArgs e)
{
(sender as Menu).Name = "MainMenu";
}
Or if the control is the child of another control:
(ControlStack.Children[0] as Menu).Name = "MainMenu";
But i cannot think of anything useful that could be achieved by that...
You probably just want to get a reference to the object which you can easily store in a class member. In some cases you can also slice up your XAML using resources.
e.g.
<local:SomethingIWouldLikeToReference x:Key="SomethingIWouldLikeToReference"/>
<local:UserControl x:Name="userControl">
<Stuff>
<MoreStuff Content="{StaticResource SomethingIWouldLikeToReference}"/>
</Stuff>
</local:UserControl>
public MainWindow()
{
InitializeComponent();
MyReference = FindResource("SomethingIWouldLikeToReference") as SomethingIWouldLikeToReference;
}
Example if I have ListView Control and I want to use it to add items and remove items
Make private ListView and initialize it
ListView temp_control_List = new ListView()
then make loaded Eventhandler from Blend so it will be in VS then
private void ListView_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
temp_control_List = sender as ListView;
}
Now you can add and remove to and from the list view control from temp_control_List

Detect change in RadGrid item expand/collapse state

I have a RadGrid displaying a hierarchical structure. I want to save the expand/collapse state of each item in the grid so that when the user returns to the site, everything looks exactly as they left it. I have code to save and restore the state of expanded/collapsed items, however, I now need a way to detect which item is currently being expanded/collapsed when the user clicks on the expand/collapse icon. I know there's a command event, but there is no command argument nor does there seem to be any indication of which item's state is being changed. Any ideas?
I found that the GridCommandEventArgs passed into the RadGrid.ItemCommand event contains an 'Item' property that represents the item that was expanded/collapsed. If casted to a GridDataItem, one can retrieve the data key and do whatever manipulation is necessary.
protected void RadGrid_OnItemCommand(object sender, GridCommandEventArgs e)
{
try
{
if(e.CommandName.Equals("ExpandCollapse"))
{
string id = ((GridDataItem)e.Item).GetDataKeyValue("ID").ToString();
// ... do work on id (i.e. save state, etc.) ...
}
} catch(Exception ex)
{
// What could possibly go wrong? :)
}
}
Use Persistance Framework.
In my post here:
http://www.telerik.com/community/forums/wpf/persistence-framework/presistenceframework-not-save-groups.aspx#2248287
there is example how to do this.

Is there an easy/built-in way to get an exact copy (clone) of a XAML element?

I need to make areas of XAML printable and so have make this button handler:
private void Button_Click_Print(object sender, RoutedEventArgs e)
{
Customer.PrintReport(PrintableArea);
}
And in PrintReport I pack the frameworkelement into other elements in order to print it in a slightly different way than it is on the screen, like this:
public void PrintReport(FrameworkElement fwe)
{
StackPanel sp = new StackPanel();
sp.Children.Add(fwe);
TextBlock tb = new TextBlock();
tb.Text = "hello";
sp.Children.Add(tb);
PrintDialog dialog = new PrintDialog();
if (dialog.ShowDialog() == true)
{
dialog.PrintVisual(sp, "Print job");
}
}
But the above gives me the following error:
Specified element is already the
logical child of another element.
Disconnect it first.
Is there an easy way to clone the FrameworkElement so that I can manipulate the copy, print it, and then forget about it, leaving the original element in the XAML being displayed on the screen intact?
Something like this I would imagine:
FrameworkElement fwe2 = FrameworkElement.Clone(fwe); //pseudo-code
I had a similar problem in my current project and solved it with this code.
public static class ExtensionMethods
{
public static T XamlClone<T>(this T original)
where T : class
{
if (original == null)
return null;
object clone;
using (var stream = new MemoryStream())
{
XamlWriter.Save(original, stream);
stream.Seek(0, SeekOrigin.Begin);
clone = XamlReader.Load(stream);
}
if (clone is T)
return (T)clone;
else
return null;
}
}
This way it simply appears as a method on all objects in your WPF project, you do not need to give any parameters to the method, and it returns an object of the same class as the original.
In WPF, copying (or "cloning") elements is almost never correct. This effectively makes this an XY Problem question. I.e. you only think that you need to literally clone the elements in your visual tree. But you don't.
The idiomatic and correct approach here is to declare a DataTemplate that represents the data you want to print. Of course, that also means that the data you want to print is in turn being represented by a view model class, for which the DataTemplate has been declared (i.e. through the DataType property).
For example:
<DataTemplate DataType={x:Type PrintableViewModel}>
<!-- template contents go here -->
</DataTemplate>
The PrintableViewModel class being, of course, a view model class containing the data you want to use to populate the visual tree that will be printed.
In the XAML for your UI, you'd then use it something like this:
<ContentControl Content={Binding PrintableViewModelProperty}/>
I.e. bind the Content property to a property in the current DataContext object that returns an instance of your PrintableViewModel, and let the ContentControl display the data appropriately.
WPF will look up the appropriate data template and apply it for display in the ContentControl. When you want to print the data, you then just do something like this:
PrintDialog printDialog = new PrintDialog();
if (printDialog.ShowDialog() == true)
{
ContentControl contentControl = new ContentControl { Content = ((ViewModelClass)DataContext)PrintableViewModelProperty};
// This part with the margins is not strictly relevant to your question per se,
// but it's useful enough to be worth including here for future reference
PageImageableArea area = printDialog.PrintQueue.GetPrintCapabilities(printDialog.PrintTicket).PageImageableArea;
contentControl.Margin = new Thickness(area.OriginWidth, area.OriginHeight,
printDialog.PrintableAreaWidth - area.ExtentWidth - area.OriginWidth,
printDialog.PrintableAreaHeight - area.ExtentHeight - area.OriginHeight);
// This shows retrieving the data template which is declared using the DataType
// property. Of course, if you simply declare a key and reference it explicitly
// in XAML, you can just use the key itself here.
DataTemplateKey key = new DataTemplateKey(typeof(MazeViewModel));
contentControl.ContentTemplate = (DataTemplate)FindResource(key);
printDialog.PrintVisual(contentControl, "MazeGenerator");
}
This will cause WPF to automatically reuse the template you've already described for the PrintableViewModel class, populating the ContentControl's visual sub-tree according to that template, duplicating the visual you're displaying on the screen, but without having to do any sort of explicit cloning of UI elements.
The above illustrates how to reuse the visual representation exactly. But of course if you have a desire to customize the output for the purpose of printing, it's as simple as declaring a different DataTemplate to be used when you print.
I'm sure there should be easy way to do the copy (other than detaching from parent, printing and attaching back). For example you could try XamlWriter to write xaml, and then read it back via XamlReader. But I suspect there may be some binding and layout errors this way.
Instead I would try to use WriteableBitmap to take a snapshot of printable area and print it. This way you create raster and loose vector, but I'm not good enough in printing to say if it's good or bad. Anyway you could try and check :).
Cheers, Anvaka.

Categories