Creating a customizable grid overlay - wpf

Just looking for some input as to what control I should go with or a broad approach. I am going to load up a png in the program I am writing. Then I could specify that I want 32x32 lines split over the picture (I'm not breaking the picture up, just specifying a grid to be on top). So, obviously I am going to need something which I can select multiples of these "cells" (which the grid or whatever broke into) and easily identify which the user is selecting. Does the grid do this or is it something more like creating guidelines and then creating some rectangles or something?

You would use an ItemsControl or derived class such as the Selector with the ItemsPanel property set to a Grid. In the ItemsContainerStyle property would set the Style for a ContentControl. The ContentControl is a the of object that will be generated for each item in the list that your ItemsControl will be bound against using the ItemsSource property. In that style you will setup a ControlTemplate for the ContentControlto soemthing that includes a Border or Rectangle or similar to get the grid lines. The root Control in your ControlTempalte will have the Grid.Row and Grid.Column properties bound against the .Row and .Column properties of your dataitems wich will be the DataContext.
Finally you bind the ItemsControl agains an ObservableCollection of these DataItems.
<ItemsControl ItemsSource={Binding MyDataItems} ....
Your DataItem would look something like this:
public class DataItem : INotifyPropertyChanged
{
public int Row
{
get { // return field }
set { // raise the PropertyChanged event here }
}
public int Column
{
get { // return field }
set { // raise the PropertyChanged event here }
}
}

Related

DataContext for binding a UserControl with ViewModel within DataTemplate

What I'm trying to achieve is:
Have a ListView bound to an ObservableCollection of ItemRecords.
Have a TabControl that contains detailed view for all the ItemRecords in the ListView that were selected for editing.
Each TabItem contains a UserControl ("ItemInfo") that uses ItemInfoViewModel as its VM (and, not so coincidentally, DataContext).
ItemInfo UserControl needs to be populated with the data from the corresponding ItemRecord.
To achieve that, I'm trying to pass the ItemRecord (selected in the ListView) to ItemInfoViewModel.
Finally, the question: what do you think would be the best way to do this, without breaking the MVVM pattern?
The not-so-elegant way that I see (and it actually doesn't exactly follow the MVVM principles) is to have a DependencyProperty ItemRecord defined in the UserControl, provide its value via binding, and in the constructor (in the UserControl's code-behind) pass the ItemRecord to the VM (which we get by casting the DataContext).
The other problem is with how to actually pass the ItemRecord via binding.
Once I set the VM as the UserControl's DataContext, I cannot just use {Binding} to specify the current item in TabControl's source collection.
At the moment I am binding to the TabControl's SelectedItem using ElementName - but this doesn't sound too robust :-(
<localControls:TabControl.ContentTemplate>
<DataTemplate>
<ScrollViewer>
<localControls:ItemInfo ItemRecord="{Binding ElementName=Tabs, Path=SelectedItem}"/>
</ScrollViewer>
</DataTemplate>
</localControls:TabControl.ContentTemplate>
Any good advice will be greatly appreciated!
Alex
I think your problem is you're not quite understanding the MVVM pattern here; you're still looking at this as the different controls talking to each other. Where in MVVM, they should not be, each control is communicating with the view model independently of all the others. And the view model controls (and supplies) the logic which tells the controls how to behave.
So, ideally you would have something like:
public ObservableCollection<ItemRecord> ListViewRecords
{
get { ... }
set { ... }
}
public IEnumerable<ItemRecord> SelectedListViewRecords {
{
get { ... }
set { ... }
}
The ListViewRecords would be bound to the ItemsSource property of your ListView (the actual properties might vary based on the specific controls you're using, I'm used to the Telerik suite at the moment so that's where my head is). And the SelectedListViewRecords would be bound to the SelectedItems property of the ListView. Then for your TabControl you would have:
public ObservableCollection<MyTabItem> Tabs
{
get { ... }
set { ... }
}
public MyTabItem SelectedTab
{
get { ... }
set { ... }
}
Again, you would bind the Items property to the Tabs and SelectedItem to the SelectedTab on your TabControl. Now your view model contains all the logic, so in your SelectedListViewRecords you might do something like this:
public IEnumerable<ItemRecord> SelectedListViewRecords {
{
get { ... }
set
{
_selectedRecords = value;
NotifyPropertyChanged("SelectedListViewRecords");
Tabs.Clear(); // Clear the existing tabs
// Create a new tab for each newly selected record
foreach(ItemRecord record in value)
Tabs.Add(new MyTabItem(record));
}
}
So the idea here is that the controls do nothing more than send and receive property changes, they know nothing of the underlying data, logic, etc. They simply show what their bound properties tell them to show.

Silverlight DataGrid and Binding columns

I have a silverlight datagrid control and columns autogenerate property is set to false.
I am using MVVM and wants to bind the columns collection.
The data which i get is from xml. Something similar to sample code
Link
Now by passing the datagrid control from xmal file to the modelview I can get the expected behavior but with that way , i am adding columns in the datagrid control.
Is there any way, so that I can bind the columns collection with the datagrid control so that no need to pass the control to view model.
-Rajesh
Sounds like a strange implementation of MVVM you have there. Your ViewModel should be completely independent of the View. If you want to create dynamic columns on the grid then why not expose a relevant property collection on the ViewModel, and iterate through it in a relevant method on the View code behind e.g.
//Used with an Infragistics XamWebGrid control
private void BuildGrid() {
foreach (var dataItem in **ViewModel**.MyDataCollection)
{
var myCol = new TemplateColumn
{
HeaderText = dataItem.ItemNm,
Key = dataItem.PrimaryKey
};
MyGrid.Columns.Add(myCol);
}
}
You really shouldn't be passing controls from the View over to the ViewModel. The ViewModel should have no knowledge of any controls on the View.

WPF Accessing Items inside Listbox which has a UserControl as ItemTemplate

I have Window that has a ListBox
ListBox(MyListBox) has a DataTable for its DataContext
ListBox's ItemSource is : {Binding}
Listbox has a UserControl(MyUserControl) as DataTemplate
UserControl has RadioButtons and TextBoxes (At first They're filled with values from DataTable and then user can change them)
Window has one Submit Button
What I want to do is, when user clicks the submit button
For each ListBox Item, get the values form UserControl's TextBoxes and RadioButtons.
I was using that method for this job :
foreach(var element in MyListBox.Items)
{
var border = MyListBox.ItemContainerGenerator.ContainerFromItem(element)as FrameworkElement;
MyUserControl currentControl = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(myBorder,0) as Border,0)as ContentPresenter,0)as MyUserControl;
//And use currentControl
}
I realised nothing when using 3-5 items in Listbox. But when I used much more items, I saw that "var border" gets "null" after some elements looped in foreach function.
I found the reason here :
ListView.ItemContainerGenerator.ContainerFromItem(item) return null after 20 items
So what can I do now? I want to access all items and get their values sitting on user controls.
Thanks
You should use objects who implement INotifyPropertyChanged and bind an ObservableCollection of it to the ItemSource
And then you can get all the list of items.
Here some quick links from MSDN to get more informations
How to: Implement Property Change Notification
Binding Sources Overview
You should google for some tutorials about this.
Zied's post is a solution for this problem. But I did the following for my project:
I realised that there's no need to use UserControl as DataTemplate in my project. So I removed ListBox's DataTemplate.
I removed MyListBox.DataContext = myDataTable and used this:
foreach(DataRow dr in myDataTable.Rows)
{
MyUserControl muc = new MyUserControl(dr);
myListBox.Items.Add(muc);
}
I took DataRow in my UserControl's constructor and did what I want.
And at last I could access my UserControls in ListBox by using :
foreach(MyUserControl muc in
myListBox)
{
//do what you want
}
Easy huh? :)

Binding to an ObservableCollection of UserControls

Simple Silverlight question: I have an ObservableCollection<MyObject> in my viewmodel. Every MyObject has a Label property. If I bind a ListBox to the collection and set DisplayMemberPath to Label, or set the ItemTemplate to a TextBlock that binds the Text property to Label, all works as expected.
If I change MyObject so it derives from a UserControl, the Label text no longer shows up in the ListBox; each item just shows up as a blank strip a few pixels tall. Why is this? There's obviously something I'm missing here about how different things get rendered.
The ListBox determines that the set of items in its ItemsSource are already UIElement instances and therefore decides to use those elements directly as the content of the ListBoxItem elements it creates.

Is there a way to logically group controls in WPF

Here's the scenario
I have a Grid with some TextBlock controls, each in a separate cell in the grid. Logically I want to be able to set the Visibility on them bound to a property in my ViewModel. But since they're each in a separate cell in the grid, I have to set each TextBlock's Visibility property.
Is there a way of having a non-visual group on which I can set common properties of its children? Or am I dreaming?
There is no non-visual group that would make this possible.
Setting the Visibility properties, directly or in a common Style shared by all of the TextBlocks, is probably the simplest solution.
Another option is to bind the visibility property of each item in your group of items to one single item, that way in your code behind you are only ever having to set the visibility of one item.
If possible I mostly place them in a GroupBox and set the groupbox BorderThickness to 0. That way all controls are grouped, you don't see that it's a groupbox and you can set the visibility with one property..
<Style TargetType="{x:Type GroupBox}"
x:Key="HiddenGroupBox">
<Setter Property="BorderThickness"
Value="0" />
I hope you have defined all of your cell UI elements inside a DataTemplate. You can do a small trick at the ViewModel level to achieve what you are looking for.
Have Singleton class at the ViewModel, which should have the Visibility or an equivalent property which you wanted to bind to every TextBlock.
The Singleton class should implement INotifypropertyChanged to get the change notification to the UI
Bind the Singleton property in the XAML and control this property from anywhere in your application.
< TextBlock Visibility="{Binding Source={x:Static local:Singleton.Instance},Path=Visibility}"
And a simple Singleton class can be implemented as
public class Singleton :INotifyPropertyChanged
{
private Singleton() { }
public static Singleton Instance
{
get
{
if (instance == null){ instance = new Singleton(); }
return instance;
}
}
private Visibility _visibility;
public Visibility Visibility
{
get { return _visibility; }
set
{
_visibility = value;
PropertyChanged( this, new PropertyChangedEventArgs("Visibility") );
}
}
public event PropertyChangedEventHandler PropertyChanged;
private static Singleton instance;
}
Now you can control Singleton.Instance.Visibility = Visibility.Collapsed anywhere from your code behind
It may be possible to make a custom control that redirects all its add/remove children methods to its own parent, while still keeping a record of its contents so it can apply its own property styles. Would be tricky though.
I realise that this a very ancient question, but there will no doubt people finding this thread after searching for something related. Therefore I offer the following very simple solution:
Place all of the controls in question into a new grid that sits within the existing grid; spans the appropriate cells and replicates them within it's own structure. Then you can change the visibility of the new grid, and with it the controls inside.

Resources