Silverlight - ListBox with a button dynamically bound - how do I get the original list item data? - silverlight-3.0

I've got a ListBox, within it I have a custom DataTemplate that builds a button. Each button represents a selection the user can perform.
When the button is clicked, is there anyway I can retrieve the original data record for that bound item? In standard C#, I can create a button and use CommandArgument to pass an ID to an event. Is there something similar in Silverlight?
Thanks

private void RemoveMember_Click(object sender, RoutedEventArgs e)
{
var employee = ((Button)sender).DataContext as Employee;
if(employee == null)
return;
_employeeList.Items.Remove(employee);
}

Related

Using DockPanelSuite, how do you get context menu for tab strip separate from document tab?

When using DockPanelSuite, is it possible to have a context menu for the tab strip that is different from the one for a document tab? For example, right click an empty space on the tab strip and get one context menu then right click a document tab and get a different context menu specific to the document.
I tried setting the ContextMenuStrip property of the DockPanel. I got a context menu for any empty space on the DockPanel control as well as the document tab strip when visible and all open document tabs. That's a good start but I really only wanted the context menu for the tab strip. Not the main control or any tabs.
I also followed along with the sample project to make a context menu for the document by setting the TabPageContextMenuStrip property of the DockContent form. I discovered that you get a document specific context menu by right clicking the document tab, but it also overrides the DockPanel's ContextMenuStrip. While that is useful, it's still not the desired result.
Edit:
Updating this post in case anyone else is interested in achieving the objective of the question.
After much source code analysis and testing, I concluded that the objective could not be achieved using the available public Properties, Methods, and Events. However, we can achieve the goal by using a bit of reflection.
Discoveries:
DockContent.ContextMenuStrip
This property does nothing for the DockPanel. It will provide a context menu in the client area of the document. However, for some reason, the RichTextBox control set to Fill in the provided sample blocks the context menu from popping up.
DockContent.TabPageContextMenuStrip
This property causes the associated ContextMenuStrip to display when the document is active. However, it displays when you right click anywhere on the tab strip, not just when you right click the document tab.
Solution:
First, add a public property to the DockContent form which will contain a reference to the context menu.
public ContextMenuStrip TabContextMenu { get { return contextMenuTabPage; } }
Next, add an event handler in the MDI main form for the DockPanel.ActiveDocmentChanged event. This will be used to add an event handler to the tab strip after it’s been created.
this.dockPanel.ActiveDocumentChanged += new System.EventHandler(this.dockPanel_ActiveDocumentChanged);
private void dockPanel_ActiveDocumentChanged(object sender, EventArgs e)
{
// Hook into the document pane tabstrip mouse up event
// if we haven't already.
if (dockPanel.ActiveDocumentPane != null
&& dockPanel.ActiveDocumentPane.TabStripControl != null
&& dockPanel.ActiveDocumentPane.TabStripControl.Tag == null)
{
dockPanel.ActiveDocumentPane.TabStripControl.Tag = "MouseUp Hooked";
dockPanel.ActiveDocumentPane.TabStripControl.MouseUp +=
TabStripControl_MouseUp;
}
}
Finally, add the event handler for the TabStripControl.
private void TabStripControl_MouseUp(object sender, MouseEventArgs e)
{
// Capture right click action
if (e.Button == MouseButtons.Right)
{
ContextMenuStrip menu = contextMenuDocumentPane;
Point screenPos = Cursor.Position;
Point tabstripsPos = dockPanel.ActiveDocumentPane
.TabStripControl.PointToClient(screenPos);
// Determine if cursor is over a tab
var tabstrip = dockPanel.ActiveDocumentPane.TabStripControl;
var tabs = tabstrip.GetType()
.GetProperty("Tabs", BindingFlags.Instance |
BindingFlags.NonPublic).GetValue(tabstrip);
foreach (var tab in (IEnumerable)tabs)
{
var bounds = tab.GetType()
.GetProperty("Rectangle")
.GetValue(tab);
if (((Rectangle)bounds).Contains(tabstripsPos))
{
// Display context menu for this document tab
var document = tab.GetType()
.GetProperty("Content")
.GetValue(tab);
menu = ((ContentWindow)document).TabContextMenu;
}
}
// Show appropriate context menu
menu.Show(screenPos);
}
}

ICommand not always firing when tab selected

I have a simple ActionBar with 3 tabs attached. When a tab is clicked, the fragment is inflated and the view shows. The tab being click event is fired using an event. Initially, the first fragment is inflated, but the others respond and inflate if clicked.
If I change the event being fired to an ICommand, only the last fragment is inflated and then if I click on the first tab, that and the last are inflated. Never the second.
My code is this
ICommand TabClicked
{
get
{
return new RelayCommand(() =>
{
tab.TabSelected += (object sender, ActionBar.TabEventArgs e) => TabOnTabSelected(sender, e);
});
}
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.Main);
ActionBar.NavigationMode = ActionBarNavigationMode.Tabs;
fragments.Add(new TODFragment());
fragments.Add(new ConditionsFragment());
fragments.Add(new ResultsFragment());
AddTabToActionBar("Time", Resource.Drawable.crucifix_colour);
AddTabToActionBar("Conditions", Resource.Drawable.weather_colour);
AddTabToActionBar("Results", Resource.Drawable.tod_colour);
}
void AddTabToActionBar(string text, int iconResourceId)
{
tab = ActionBar.NewTab().SetTag(text).SetText(text).SetIcon(iconResourceId);
/* uncomment and comment out one of the two below to see the difference in operation */
tab.TabSelected += TabOnTabSelected;
//tab.SetCommand<ActionBar.TabEventArgs>("TabSelected", TabClicked);
ActionBar.AddTab(tab);
}
void TabOnTabSelected(object sender, ActionBar.TabEventArgs tabEventArgs)
{
var tabNo = sender as ActionBar.Tab;
var frag = fragments[tabNo.Position];
tabEventArgs.FragmentTransaction.Replace(Resource.Id.frameLayout1, frag);
}
Am I missing something fundamental here in the difference between ICommands and Events or is it something else?
I'm using Xam.Android and MVVMLight
I found the answer. When I create the partial class I define the UI objects like this (or something like this at least)
EditText myEditText;
EditText MyEditText = myEditText ?? (view.FindViewById<EditText>(Resources.Id.myEdit);
This is fine, but it does mean that once defined, it doesn't get redefined.
Not a problem if the UI is not really going to change, but every time an action tab is pressed, the fragment is refreshed. Only problem is the Id isn't changing as myEditText is not null.
The answer is add a method in the UI definition code that nulls the objects then in the main code, when the UI disappears, call the nulling method. Everything works then

ListGrid put focus in the FilterEditor

I have a ListGrid defined like this:
ListGrid lgrid = new ListGrid();
ListGridField first = new ListGridField("first",first");
ListGridField second = new ListGridField("second ",second ");
lgrid.setFields(first, second);
lgrid.setShowFilterEditor(true);
¿How can i put the keyboard focus in the first filter editor field after i call show() in the layout?
Thxs in advance.
Depending on what your use case is (which would be useful to provide a more focused answer), the solution you posted might not be what you really need, because if you scroll on your ListGrid, it could trigger a new data fetch (if there are more records to show), and move the cursor to the filter editor as a result (if your user is editing some records at that point, the cursor moving to the filter row is not what she would want to happen!!).
In such a case, you probably just want to call grid.focusInFilterEditor("fieldToFocus") after the listGrid.show() statement or in the ClickHandler of some button you use to fetch the data, etc.
Anyway, you don't need the Timer either. This works:
listGrid.addDataArrivedHandler(new DataArrivedHandler() {
#Override
public void onDataArrived(DataArrivedEvent event) {
grid.focusInFilterEditor("fieldToFocus");
}
});
I got the solution, its focusInFilterEditor, this is an example to set the focus after the data arrived to the grid:
// Put the focus on the first listGrid field when is loaded
listGrid.addDataArrivedHandler(new DataArrivedHandler() {
#Override
public void onDataArrived(DataArrivedEvent event) {
Timer t = new Timer() {
public void run() {
if(listGrid.getFilterEditorCriteria() == null){
listGrid.focusInFilterEditor("fieldToFocus");
}
}
};
t.schedule(600);
}
});

Asp.Net MVC: GridView Paging Issue

I have a problem with the gridview control. I've created a form with a dropdownlist, a retrieve button and a gridview control. I choose a value from the dropdownlist, i click the button and the gridview is filled with the data in 2 pages. When i click on the second page the data are gone. I must press again the retrieve button and then the second page is shown correctly. I have used the PageIndexChanging but its nothing changed.Any ideas?
Be sure his data source hasn't been reset on postback.
protected void Page_Load(object sender, EventArgs e)
{
if (!this.IsPostBack)
{
this.GridView1.DataSource = new DirectoryInfo("C:\\windows").GetFiles();
this.GridView1.PageSize = 3;
this.GridView1.AllowPaging = true;
this.GridView1.DataBind();
}
}
Implement PageIndexChanging method to the grid... like example.
protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
this.GridView1.PageIndex = e.NewPageIndex;
}

Nested Silverlight Datagrid - Row Details works great, but I want a button!

I'm using a silverlight 3 datagrid, and within it, I'm nesting related records in another control by using the rowdetails (visibilitymode = visiblewhenselected).
I really like how this works, but I'd much rather have the grid display the row details when a "+" button is pressed, much as a tree will expand when you click a node.
I tried programmatically defining the template by using resources like this:
<Grid.Resources>
<DataTemplate x:Key="EmptyTemplate">
<StackPanel>
<!--<TextBlock Text="Empty Template!!!" />-->
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="SongTemplate">
<StackPanel>
<AdminControls:ArtistSongControl x:Name="ArtistSongControl" />
</Stack>
</DataTemplate>
</Grid.Resources>
And in the grid's LoadingRowDetails event, I'd choose which template to set by:
e.Row.DetailsTemplate = (DataTemplate)LayoutRoot.Resources["SongTemplate"];
This sortof worked, but I found that I had problems with collapsing previous rows details template, and even crashed ie8 (not sure if that's related).
Basically, I really like how the silverlight 3 datagrid works, and even how the rowdetailstemplate stuff is implemented. I simply would like to defer loading any details until a row is expanded purposely (as a tree would be). All of the 3rd party grids seem to do this, and microsoft's is soooo close. Does anyone have any idea how to solve this one?
Thanks, Dennis
Dennis,
In case you haven't already found an answer to this, I wanted the same behavior and solved it by customizing the RowHeaderTemplate, which lets you throw a button in the header for each row. Then I implemented a handler for the button like so:
private void ToggleButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
ToggleButton button = sender as ToggleButton;
DataGridRow row = button.GetVisualAncestorOfType<DataGridRow>();
if (button.IsChecked == true)
{
row.DetailsVisibility = Visibility.Visible;
//Hide any already expanded row. We only want one expanded at a time for simplicity and
//because it masks a virtualization bug in the datagrid.
if (_expandedRow != null)
_expandedRow.DetailsVisibility = Visibility.Collapsed;
_expandedRow = row;
}
else
{
row.DetailsVisibility = Visibility.Collapsed;
_expandedRow = null;
}
}
Note that GetVisualAncestorOfType<> is an extension method I've implemented to dig into the visual tree.
You'll also need to set the datagrid's HeadersVisibility property to Row or All
here is another way to achieve what you are trying to do:
In the DataGrid set up a LoadingRow Event like this:
<data:DataGrid LoadingRow="ItemsGrid_LoadingRow" .....
In the DataGrid create a Template Column which will contain a Button such as the following:
<data:DataGridTemplateColumn CellStyle="{StaticResource DataGridCellStyle1}" CanUserReorder="False">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="ViewButton" Click="ToggleRowDetailsVisibility" Cursor="Hand" Content="View Details" />
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
In the LoadingRow Event locate the button that is (in this case) stored in the first column of the DataGrid, then store the current DataGridRow into the buttons Tag element
private void ItemsGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
var ViewButton = (Button)ItemsGrid.Columns[0].GetCellContent(e.Row).FindName("ViewButton");
ViewButton.Tag = e.Row;
}
In the Buttons EventHandler (in this case ToggleRowDetailsVisibility) we will extract the Row so that we can toggle its DetailsVisibility
In the LoadingRow Event locate the button that is (in this case) stored in the first column of the DataGrid, then store the current DataGridRow into the buttons Tag element
private void ToggleRowDetailsVisibility(object sender, RoutedEventArgs e)
{
var Button = sender as Button;
var Row = Button.Tag as DataGridRow;
if(Row != null)
{
if(Row.DetailsVisibility == Visibility.Collapsed)
{
Row.DetailsVisibility = Visibility.Visible;
//Hide any already expanded row. We only want one expanded at a time for simplicity and
//because it masks a virtualization bug in the datagrid.
if (CurrentlyExpandedRow != null)
{
CurrentlyExpandedRow.DetailsVisibility = Visibility.Collapsed;
}
CurrentlyExpandedRow = Row;
}
else
{
Row.DetailsVisibility = Visibility.Collapsed;
CurrentlyExpandedRow = null;
}
}
}
You will notice that "CurrentlyExpandedRow", this is a Global variable, of type DataGridRow, that we store the currently expanded row in, this allows us to close that Row when a new one is to be opened.
Hope this helps.
In addition to the answer uxrx provided here is the code for finding an ancestor
public static partial class Extensions
{
public static T FindAncestor<T>(DependencyObject obj) where T : DependencyObject
{
while (obj != null)
{
T o = obj as T;
if (o != null)
return o;
obj = VisualTreeHelper.GetParent(obj);
}
return null;
}
public static T FindAncestor<T>(this UIElement obj) where T : UIElement
{
return FindAncestor<T>((DependencyObject)obj);
}
}
For this:
DataGridRow row = button.GetVisualAncestorOfType<DataGridRow>();
We can use as:
HyperlinkButton button = sender as HyperlinkButton;
DataGridRow Row = DataGridRow.GetRowContainingElement(button)
I suggest you take a look at the RowVisibilityChanged event on the datagrid, basically when the row visibility changes to "Visible", then load the info for the row.

Resources