I have a DataTemplate which requires an event handler for one of the objects. This DataTemplate is contained in a ResourceDictionary. What is the best way to add an event handler to this template?
I tried defining the event handler in app.xaml.cs but the handler isn't executing. Creating a code behind file for the ResourceDictionary leads to load errors during app start up in MergedDictionaries.
from GraphStyles.xaml
<DataTemplate x:Key="PieTemplate">
<Grid HorizontalAlignment="Left" Width="350" Height="350" >
<Border>
<Charting:Chart
x:Name="PieChart"
Title="Play Attempts"
Margin="70,0" Loaded="PieChart_Loaded">
<Charting:Chart.Series>
<Charting:PieSeries
Title="Attempts"
ItemsSource="{Binding Items}"
IndependentValueBinding="{Binding Name}"
DependentValueBinding="{Binding Value}"
IsSelectionEnabled="True" />
</Charting:Chart.Series>
</Charting:Chart>
</Border>
</Grid>
</DataTemplate>
in App.Xaml.cs
private void PieChart_Loaded(object sender, RoutedEventArgs e)
{
var pieChart = sender as Chart;
var legendItems = ((PieSeries)pieChart.Series[0]).LegendItems;
foreach (LegendItem item in legendItems)
{
pieChart.LegendItems.Add(item);
pieChart.LegendStyle = item.Style;
}
}
Option 1
As far as I am aware you will have to reference the datatemplate in the page/usercontrol's Resources at the top. Use a merged dictionary so you can still utilize the graphstyles.xaml.
If you are uncomfortable as this breaks your convention, there is a rather long winded alternative:
Option 2
Use an MVVM Viewmodel and set the page/usercontrols DataContext.
Keep the datatemplate in the graphstyles.xaml and use an Attached Behavior to hook the Loaded event, passing the event trigger up to the viewmodels command.
Create an event in the ViewModel that the UI can respond to, then hook onto that and handle in the Views codebehind as you have done.
I must say I'm not mad on Option 2 as it kind of breaks some view/VM seperation but it should get the job done - note that you will have to pass the chart as an object through from the attached behavior, to the viewmodel then back to the view against before you cast it back to a Chart.
Related
Original problem: I want to call a function each time the soft keyboard is shown/hidden on my Xamarin.Forms ContentPage.
One option was to use a custom renderer where I have access to the keyboard observers. I haven't tried this yet; I thought I'd try banging my head with other approaches first.
Approach 1) Assign event handler to the Focused/Unfocused events of all entries on my page which trigger a keyboard show/hide, in the page's code-behind as follows:
EventHandler<FocusEventArgs> keyboard_handler = (object sender, FocusEventArgs e) =>
{
var binding_context = BindingContext as MyViewModel;
binding_context?.OnKeyboardShowing();
};
PasswordEntry.Focused += keyboard_handler; // doesn't trigger keyboard_handler
PasswordEntry.Unfocused += keyboard_handler; // doesn't trigger
UsernameEntry.Focused += keyboard_handler; // triggers
UsernameEntry.Unfocused += keyboard_handler; //doesn't trigger
Question 1: Why don't some of those trigger?
Approach 2) Use EventTriggers along with a custom TriggerAction<Entry> class, as follows:
Part of the XAML file showing one of the entries (which is in a grid, in a StackLayout in a ScrollView in a Grid, if you must know):
<Entry Grid.Row="0"
x:Name="UsernameEntry"
Margin="0,2"
HorizontalOptions="FillAndExpand"
Placeholder="User"
IsVisible="{Binding UsernameEnabled}"
Completed="Username_Completed"
IsEnabled="{Binding EnableControls}"
Text="{Binding Username, Mode=TwoWay}" >
<Entry.Triggers>
<EventTrigger Event="Focused">
<local:FocusedAction />
</EventTrigger>
</Entry.Triggers>
</Entry>
And the TriggerAction<Entry> class defined in the code-behind:
public class FocusedAction : TriggerAction<Entry>
{
protected override void Invoke(Entry entry)
{
Debug.WriteLine("Reached"); // Except it never reaches here.
}
}
Question 2: Why doesn't Invoke up there ever get triggered?
Am I missing something in my XAML, code-behind or somewhere else? I've checked the Xamarin docs, but as always, I've found them less than useless.
I have a form that has a layout like so:
<cms:FormField runat="server" ID="fMemberType" Field="MemberType" />
<cms:FormField runat="server" ID="fEmployeeCount" Field="EmployeeCount" />
<asp:Literal runat="server" ID="test" Text="test" />
<script runat="server">
protected void Page_PreRender(object sender, EventArgs e)
{
test.Text = fMemberType.Value.ToString();
}
</script>
However this produces Object reference not set to an instance of an object. because it can't find fMemberType for some reason. Looking for the correct way of doing this.
It's worth noting that the form fields are dropdowns with depending flags set so changing them triggers a postback, or at least it would, but I set the webpart container to be an update panel so it's AJAXing which means the data isn't available in the page POST params. I could turn this off and grab the data from the POST data but wanted to know if there was a better way first.
So you're fully defining the fields and everything for your form? Why not use the DataForm control and dynamically create the form for you? You can then get the data like so: (formUserSettings is a cms:DataForm)
EditingFormControl ctrState = formUserSettings.BasicForm.FieldEditingControls["UserState"] as EditingFormControl;
Then do some checking and assign the value:
if (ctrState != null)
{
fState = ctrlState.Value;
}
Most likely the form value is not set until after the pre-render. Alen Genzic's recommendation will show that. May want to try OnInit.
I have a PXNumberEdit field which, on enter, adds a product to a grid on a customized SO301000 page. The insert works and the field
is emptied after the product is added. However, I would like to return the focus to that field. There doesn't seem to be
a SetFocus method for the field.
I have tried using the SO301000.cs code behind to set the focus, by adding a function for onValueChanged
to save the object as session variable and on pageload to set the focus on the saved object. This causes
the page to never finish loading.
I have also tried to use jquery in various ways but that hasn't worked either. Is there a way to do this?
The Hack
There is no SetFocus method in the framework like there is for SetEnabled/SetDisplayName/SetVisibility because most events are raised on focus changes and the framework ensures that the focus is not lost on every record updates. To set it up manually, you will then need to wait that the callback is completed before setting the focus.
To do so you will need to add a Javascript delegate to the list of event handlers to be called once the callback is over. The following code will set the focus on Customer Reference Nbr. every time Customer ID is changed (in SO301000):
<script type="text/javascript">
function FormView_Load() {
px_callback.addHandler(setFocusOnCustRef);
return;
}
var setFocus = false;
function CustomerID_ValueChanged() {
setFocus = true;
return;
}
function setFocusOnCustRef(context, error) {
if (setFocus === true)
{
setFocus = false;
var refNbr = px_alls["edCustomerRefNbr"];
refNbr.focus();
}
return;
}
</script>
<px:PXFormView ID="form" runat="server" DataSourceID="ds" Style="z-index: 100" Width="100%" DataMember="Document" Caption="Order Summary"
NoteIndicator="True" FilesIndicator="True" LinkIndicator="True" EmailingGraph="PX.Objects.CR.CREmailActivityMaint,PX.Objects"
ActivityIndicator="True" ActivityField="NoteActivity" DefaultControlID="edOrderType" NotifyIndicator="True"
TabIndex="14900" ClientEvents-Initialize="FormView_Load">
...
<px:PXSegmentMask CommitChanges="True" ID="edCustomerID" runat="server" DataField="CustomerID" AllowAddNew="True"
AllowEdit="True" DataSourceID="ds" ClientEvents-ValueChanged="CustomerID_ValueChanged"/>
...
</px:PXFormView>
Note that I added ClientEvents-Initialize="FormView_Load" on the PXFormView and ClientEvents-ValueChanged="CustomerID_ValueChanged" on the CustomerID's PXSegmentedMask.
As you can see this is a hack... When the setFocusOnCustRef is raised we have just refreshed the record (RowSelected) and we don't know what was changed prior to that (what field has been changed? was the change canceled?). The context that is passed to the delegate is only related to re-updating the records. To get a better understanding of what events are raised and in which order, please refer to the Update Scenario Event Model:
Thoughts and Tips
I don't know much of your implementation but I would like to point out that your needs look very similar to the function Add Stock Item that opens a SmartPanel with the buttons Add/Add & Close/Cancel. If the callback is raised from a button you will have meaningful information in your context and won't need to add a Javascript event on ValueChanged.
When you Save/Cancel. The focus will return to your first form element in the tab order (if successful).
You can set the Tab Order directly in the PXUIFieldAttribute :
PXUIField(DisplayName = "Asset ID", Visibility = PXUIVisibility.SelectorVisible, TabOrder=1)]
I have been trying to figure this out for ages and I am stumped.
I have the following XAML:
<TextBox x:Name="MyTextBox" Text="{Binding MyName, Mode=TwoWay}" Width="200">
<WinRtBehaviors:Interaction.Behaviors>
<Win8nl_Behavior:EventToCommandBehavior Event="TextChanged"
Command="TextChangedCommand"
CommandParameter="{Binding Text, ElementName=MyTextBox, Mode=OneWay}"/>
</WinRtBehaviors:Interaction.Behaviors>
</TextBox>
And in my View Model:
public ICommand TextChangedCommand
{
get
{
return new RelayCommand<string>((p) =>
{
var msg = new MessageDialog(string.Format("Hi there {0}", p));
msg.ShowAsync();
});
}
}
But the string value I am hoping to appear in the CommanParameter (p) is always null.
What am I doing wrong?
The best thing you can do is to pass in the text value as the CommandParameter.
Behaviors are not part of the Visual Tree, so they don't have access to the XAML scope and the capability to perform ElementName bindings. This blog post provides more details, and a suitable solution.
I have a button control to which I want add image as well as hyperlink property, i.e it should be an image button with link to other source. I tried
<Button Click="OnNavigationRequest" ToolTip="Orkut">
<Image Source="C:\Documents and Settings\SaurabhS\My Documents\Visual Studio 2008 \Projects\SaurabhSinhaDemos\WPF_Zone\AddressBook\AddressBook\images\orkut.jpeg"/>
<Hyperlink NavigateUri="http://www.orkut.com">Orkut</Hyperlink>
</Button>
and in code behind:
AddHandler(Hyperlink.RequestNavigateEvent,
new RoutedEventHandler(OnNavigationRequest));
public void OnNavigationRequest(object sender, RoutedEventArgs e)
{
var source = e.OriginalSource as Hyperlink;
if (source != null)
Process.Start(source.NavigateUri.ToString());
}
But got the following error:
content set more than once...
How should I do it?
In your code, the Button element contains two child elements. The Button element can only take one child element.
Wrap Image and Hyperlink in a StackPanel or some other layout container and the error will go away (see Int3's answer for an example).
Try following
<Button>
<StackPanel Orientation="Horizantal">
<Image Source="path to the image"/>
<Hyperlink NavigateUri="http://www.orkut.com"/>
</StackPanel>
</Button>