I've created a Sitefinity widget with only and iframe in it.
<%# Control Language="C#" AutoEventWireup="True" CodeBehind="ApplicationFrame.ascx.cs"
Inherits="MyProject.Web.Ui.Customized.EmbeddedApplications.ApplicationFrame" %>
<iframe runat="server" id="ApplicationIFrame" height="250" width="250" scrolling="no" frameborder="0" seamless="seamless" src=""></iframe>
In the Page_Load of the widget I trie to access any properties of the server side iframe but I always get "Object reference not set to an instance of an object".
Here's the C#
namespace MyProject.Web.Ui.Customized.EmbeddedApplications
{
[ControlDesigner(typeof(ApplicationFrameDesigner))]
public partial class ApplicationFrame : System.Web.UI.UserControl
{
public string FrameSourceUrl {get;set;}
public string FrameHeight { get; set; }
public string FrameWidth { get; set; }
protected void Page_Load(object sender, EventArgs e)
{
//set the values of the iframe to the current properties
ApplicationIFrame.Attributes["src"] = FrameSourceUrl;
ApplicationIFrame.Attributes["height"] = FrameHeight;
ApplicationIFrame.Attributes["width"] = FrameWidth;
}
}
}
I recently change the project from a Website to a Web Application, but that hasn't seemed to impact the project in any way.
Other than that I can't see why this exception keeps being thrown no matter what I do.
Anyone else know what the problem might be?
Thanks,
Jacques
This may not work depending on the type of widget you have created. It would probably work for normal User controls, but not custom controls.
If you are following the sitefinity documentation, you can inherit from SimpleView. Then you have access to helper methods for retrieving controls from the template. So instead of this:
ApplicationIFrame.Attributes["src"] = FrameSourceUrl;
You can do this:
this.GetControl<HtmlControl>("ApplicationIFrame", true).Attributes["src"] = FrameSourceUrl;
Related
I'm converting a UWP app to Blazor WebAssembly with ASP.NET Core hosted.
I have my markup code in Index.razor and behind code in Index.razor.cs.
In the UWP project I opened a browser window from an onclick function like this:
var success = Windows.System.Launcher.LaunchUriAsync(targetPage);
What can I use in my Blazor project onclick event, that won't lead to "unhandled error has occurred"?
You can open a tab by making use of the 'window.open' function:
#inject IJSRuntime JSRuntime;
....
await JSRuntime.InvokeAsync<string>("open", $"https://www.mysite/mypage", "_blank");
if the page is internal to your website you can use the base uri:
#inject NavigationManager Navigation
#inject IJSRuntime JSRuntime;
...
await JSRuntime.InvokeAsync<string>("open", $"{Navigation.BaseUri}/mypage", "_blank");
You case use below way
razor file
JSRuntime.InvokeVoidAsync("OpenWindow");
html file
<script>
function OpenWindow() {
window.open('https://www.google.com', 'Test', 'width=800,height=860,scrollbars=no,toolbar=no,location=no');
return false
}
</script>
These are great answers and I took it one step further to make a re-usable component. My app uses anchors and link buttons so I have a quick-and-dirty switch for each.
ExternalLink.razor
#inject IJSRuntime JSRuntime
#if (Type == "link")
{
#(Text ?? Url)
}
else if (Type == "button")
{
<button type="button" class="btn btn-link" #onclick="OpenWindow">#(Text ?? Url)</button>
}
#code {
[Parameter]
public string Url { get; set; }
[Parameter]
public string Text { get; set; }
[Parameter]
public string Type { get; set; } = "link";
private async Task OpenWindow()
{
await JSRuntime.InvokeAsync<string>("open", Url, "_blank");
}
}
Usage
<ExternalLink Url="https://www.ourwebsite.com/" Text="Visit Us!" />
<ExternalLink Url="https://stackoverflow.com/" />
<ExternalLink Url="https://duckduckgo.com/" Type="button" />
Why not just use the build in NavigationManager?
Navigation.NavigateTo(a_url, true);
The trick is to use the force parameter.
Happy coding.
Issue Summary:
The processing logic on my new screen is working but the page doesn't show any feedback to the user (e.g. Timer doesn't show, No Red/Green checkboxes, checkboxes aren't disabled)
Issue Detail:
I'm creating a processing screen that requires separate information from the user to be used by the processing delegate. The business logic works, but the user experience isn't like other processing screens. When you click Process there is nothing shown to the user. Normally the page refreshes the grid by disabling and showing only the selected items being processed, in addition to the long operation timer being added (then replaced by a green check or red x based on whether the process was successful or failed). When I click process all the entire grid's selected column is checked, but again nothing else changes (e.g. no timer, status, nor is the grid disabled). Ultimately both process and process all perform the business logic but the user doesn't see anything to indicate said success/failure.
The screen shows all customer locations because the process is updating some statistics that we are keeping for each location based on orders that exist for each location.
My Graph
public class CalculateLocationStatsProcess : PXGraph<CalculateLocationStatsProcess>
{
public PXCancel<LocationStatsFilter> Cancel;
public PXFilter<LocationStatsFilter> filterLocStat;
public PXFilteredProcessingJoin<Location,
LocationStatsFilter,
InnerJoin<Customer, On<Customer.bAccountID, Equal<Location.bAccountID>>>,
Where<True, Equal<True>>,
OrderBy<Asc<Customer.acctCD, Asc<Location.locationCD>>>> processLocations;
public CalculateLocationStatsProcess()
{
}
public virtual void LocationStatsFilter_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
processLocations.SetProcessDelegate(
delegate (List<Location> list)
{
var newList = new List<Location>();
foreach (Location locLp in list)
{
newList.Add(locLp);
}
CalculateLocationStatsProcess.updateLocationStats(newList, filterLocStat.Current);
}
);
}
public static void updateLocationStats(List<Location> locations, LocationStatsFilter filter)
{
var graph = new PXGraph();
var locStats = new StatsHelper(graph, filter.TargetDate);
bool erred = false;
for (int iLp = 0; iLp < locations.Count; iLp++)
{
Location locationLp = locations[iLp];
PXProcessing<Location>.SetCurrentItem(locationLp);
try
{
locStats.setCommStats(locationLp);
}
catch (Exception ex)
{
erred = true;
PXProcessing<Location>.SetError(iLp, ex.Message);
}
}
locStats.StatCache.Persist(PXDBOperation.Insert);
locStats.StatCache.Persist(PXDBOperation.Update);
if (erred)
{
throw new PXException("Location(s) failed during recalculation process. View individual lines to see their specific error.");
}
}
}
My Page
<%# Page Language="C#" MasterPageFile="~/MasterPages/FormDetail.master" AutoEventWireup="true" ValidateRequest="false" CodeFile="SO509503.aspx.cs" Inherits="Page_SO509503" Title="Calculate Location Stats" %>
<%# MasterType VirtualPath="~/MasterPages/FormDetail.master" %>
<asp:Content ID="cont1" ContentPlaceHolderID="phDS" runat="Server">
<px:PXDataSource ID="ds" runat="server" Visible="True" Width="100%" PrimaryView="filterLocStat" TypeName="exampleNS.CalculateLocationStatsProcess">
<CallbackCommands>
</CallbackCommands>
</px:PXDataSource>
</asp:Content>
<asp:Content ID="cont2" ContentPlaceHolderID="phF" runat="Server">
<px:PXFormView ID="formFilter" runat="server" DataMember="filterLocStat" DataSourceID="ds" Style="z-index: 100" Width="100%" >
<Template>
<px:PXLayoutRule runat="server" ID="PXLayoutRule1" ControlSize="M" LabelsWidth="M" StartRow="true" />
<px:PXDateTimeEdit runat="server" ID="edTargetDate" DataField="TargetDate" />
</Template>
</px:PXFormView>
</asp:Content>
<asp:Content ID="cont3" ContentPlaceHolderID="phG" runat="Server">
<px:PXGrid ID="gridLocations" runat="server"
AdjustPageSize="Auto" AllowPaging="True" AllowSearch="true" DataSourceID="ds" FilesIndicator="false"
Height="400px" NoteIndicator="false" SkinID="Inquire" Style="z-index: 100" SyncPosition="true" Width="100%" >
<AutoSize Container="Window" Enabled="True" MinHeight="200" />
<Levels>
<px:PXGridLevel DataMember="processLocations">
<Columns>
<px:PXGridColumn DataField="Selected" Type="CheckBox" AllowCheckAll="true" Width="40px" />
<px:PXGridColumn DataField="Customer__AcctCD" Width="125px" />
<px:PXGridColumn DataField="LocationCD" Width="75px" />
<px:PXGridColumn DataField="Descr" Width="175px" />
</Columns>
</px:PXGridLevel>
</Levels>
</px:PXGrid>
</asp:Content>
My Filter DAC
public class LocationStatsFilter : IBqlTable
{
#region TargetDate
public abstract class targetDate : IBqlField { }
[PXDate()]
[PXUIField(DisplayName = "Target Month and Year")]
public virtual DateTime? TargetDate { get; set; }
#endregion
}
My Location Extension DAC
[PXTable(typeof(Location.locationID), typeof(Location.bAccountID), IsOptional = false)]
public class LocationExt : PXCacheExtension<Location>
{
#region Selected
public abstract class selected : IBqlField { }
[PXBool()]
[PXDefault(false)]
[PXUIField(DisplayName = "Selected")]
public virtual bool? Selected { get; set; }
#endregion
#region DateFirstService
public abstract class dateFirstService : IBqlField { }
[PXDBDate()]
[PXUIField(DisplayName = "Date of First Service")]
public virtual DateTime? DateFirstService { get; set; }
#endregion
}
I modelled my solution after several processing screens that I found, but I've looked at so many I couldn't say which ones I used as examples. I've moved the SetProcessDelegate call between the RowSelected event and the Constructor with no luck. I've attempted making the updateLocationStats function static versus not static (using the existing graph instead) with no success.
UPDATE:
Calling the updateLocationStats method directly instead of creating a
copy of the List didn't change the result.
Adding PXFilterable to the PXFilteredProcessingJoin didn't change the result
Removing the calls to locStats (creation, setCommStats(locationLp), Persist) didn't change the result
Added missing Location DAC
Attempted moving Selected from LocationExt DAC to a new LocationAlt : Location DAC with no change in result.
Added DataType="Boolean" to the selected field in the page. No change in behavior
Added BatchUpdate="True" to the PXGrid tag. No change in behavior.
Added PXProcessing.SetProcessed(); after locStats.setCommStats(locationLp); no change in behavior.
Alternate Test
public class LocationAlt : Location
{
#region Selected
public abstract class selected : IBqlField { }
[PXBool()]
[PXDefault(false)]
[PXUIField(DisplayName = "Selected")]
public virtual bool? Selected { get; set; }
#endregion
}
public class CalculateLocationStatsProcess : PXGraph<CalculateLocationStatsProcess>
{
public PXCancel<LocationStatsFilter> Cancel;
public PXFilter<LocationStatsFilter> filterLocStat;
[PXFilterable()]
public PXFilteredProcessingJoin<LocationAlt,
LocationStatsFilter,
InnerJoin<Customer, On<Customer.bAccountID, Equal<LocationAlt.bAccountID>>>,
Where<True, Equal<True>>,
OrderBy<Asc<Customer.acctCD, Asc<LocationAlt.locationCD>>>> processLocations;
public CalculateLocationStatsProcess()
{
}
public virtual void LocationStatsFilter_RowSelected(PXCache sender, PXRowSelectedEventArgs e)
{
processLocations.SetProcessDelegate(
delegate (List<LocationAlt> list)
{
CalculateLocationStatsProcess.updateLocationStats(list, filterLocStat.Current);
}
);
}
public static void updateLocationStats(List<LocationAlt> locations, LocationStatsFilter filter)
{
var graph = new PXGraph();
var locStats = new CommStatsHelper(graph, filter.TargetDate);
bool erred = false;
for (int iLp = 0; iLp < locations.Count; iLp++)
{
LocationAlt locationLp = locations[iLp];
PXProcessing<LocationAlt>.SetCurrentItem(locationLp);
try
{
locStats.setCommStats(locationLp);
}
catch (Exception ex)
{
erred = true;
PXProcessing<LocationAlt>.SetError(iLp, ex.Message);
}
}
locStats.StatCache.Persist(PXDBOperation.Insert);
locStats.StatCache.Persist(PXDBOperation.Update);
if (erred)
{
throw new PXException("Location(s) failed during recalculation process. View individual lines to see their specific error.");
}
}
}
Note that the alternate test didn't work either.
The problem being experienced is because the originating graph instance (that is the instance of the graph for which you are in when you first enter the screen) isn't maintaining scope to the execution graph instance (that is the instance of the graph which is doing the logic).
For Acumatica to put the processing status logic (e.g. Timer, Green/Red Status circles) it appears the originating graph needs an link to the graph performing the actions. This appears to be handled for you just fine when you point SetProcessDelegate to a method delegate.
In the issue here, you are creating an ad-hoc delegate (by using the delegate keyword in the constructor/rowselected event). The variable declaration (not instantiation) needs to be outside of the delegate.
Corrected Setting of Delegate
CalculateLocationStatsProcess graph = null;
processLocations.SetProcessDelegate((List<Location> list) =>
{
graph = PXGraph.CreateInstance<CalculateLocationStatsProcess>();
CalculateLocationStatsProcess.updateLocationStats(list, filterLocStat.Current);
}
);
Note the first line is before the SetProcessDelegate call. Even though the graph isn't instantiated outside the delegate, the graph pointer is created. Thus it appears when assigned a link is made and the UI updates as desired.
Additional Notes
The selected field still exists as part of the extension
I left the function static but it did work as desired if converted to non static, you simply change CalculateLocationStatsProcess.updateLocationStats(list, filterLocStat.Current); to graph.updateLocationStats(list, filterLocStat.Current);
The delegate declaration can occur in either the Constructor public CalculateLocationStatsProcess() or in the Filter's Row Selected Event Handler public virtual void LocationStatsFilter_RowSelected(PXCache sender, PXRowSelectedEventArgs e) both worked for me.
BatchUpdate="true" isn't necessary.
DataType="Boolean" isn't necessary.
Lastly, the linked Customer__AcctCD clears when the process is finished (the join doesn't appear to keep the fields populated) so some code for displaying the AcctCD on the graph will need changing. Since this wasn't the focus of the question presented, I do not plan to include that code here.
I believe you are missing the Selected data field in your DAC:
#region Selected
public abstract class selected : IBqlField
{
}
protected bool? _Selected = false;
[PXBool]
[PXDefault(false)]
[PXUIField(DisplayName = "Selected")]
public bool? Selected
{
get
{
return _Selected;
}
set
{
_Selected = value;
}
}
#endregion
Instead of using Location directly, create a DAC extension for Location (LocationStat?) where you will add the Selected field. Don't use Location in your processing screen, use the extension containing the selected field.
I'm quite new to XAML trying to make grid with Toggle buttons
Something like this:
<GridView ItemSource="{Binding ButtonCollection}">
<GridView.ItemTemplate>
<DataTemplate>
<ToggleButton Content="{Binding Label}" IsEnabled="{Binding BEnabled}" IsChecked="{Binding BChecked, Mode=TwoWay}"/>
<DataTemplate>
</GridView.ItemTemplate>
</GridView>
I have
ObservableCollection<ButtonClass> ButtonCollection
also class for buttons
class ButtonClass
{
public string Label {get; set;}
public bool BEnabled {get; set:}
public bool BChecked {get;set;}
}
binding works when page loads buttons are displayed from ObservableCollection
But I want collection to update when button IsChecked value changes
also is there any way to bind function to click method like:
Click="{Binding DoWhenClicked}"
Now it just results in error I think that is because DoWhenClicked isn't in ItemSource.
Summary:
I want to have Grid of toggle buttons that binds to some sort of list/array/collection of data with label, checked status, enabled status.
When toggle button is checked I want it to reflect in my collection.
Also I want to bind event to Click method so that i can perform operations like disable some Toggle Buttons when other button is checked.
What is good way to do this.
I noticed that you asked several question about Template 10 before, so I supposed that you also used Template 10 here for MVVM pattern.
binding works when page loads buttons are displayed from ObservableCollection But I want collection to update when button IsChecked value changes
In Template 10 project, if you want to get notified when parameter in the data model (here means your ButtonClass), you can derive your class from BindableBase. If you check the BindableBase class you will find that it has done the work of INotifyPropertyChanged, it will be much easier here deriving from BindableBase directly rather than implementing INotifyPropertyChanged by yourself.
also is there any way to bind function to click method
Also I want to bind event to Click method so that i can perform operations like disable some Toggle Buttons when other button is checked.
Instead of Click event, I personally recommend you to using Command in MVVM pattern, and you may want to know which Button is clicked, so you can use CommandParameter here. I created a blank template 10 project and here is my sample:
<GridView x:Name="gridView" RelativePanel.Below="pageHeader" Margin="16,12,0,0" ItemsSource="{x:Bind ViewModel.ButtonCollection}">
<GridView.ItemTemplate>
<DataTemplate>
<ToggleButton Width="100" Content="{Binding Label}" IsEnabled="{Binding BEnabled}"
IsChecked="{Binding BChecked, Mode=TwoWay}" Command="{Binding ToggleBtnClickCommand}"
CommandParameter="{Binding Label}" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
The ButtonClass is like this:
public class ButtonClass : BindableBase
{
public string Label { get; set; }
public bool BEnabled { get; set; }
private bool _bChecked;
public bool BChecked
{
get { return _bChecked; }
set
{
_bChecked = value;
RaisePropertyChanged();
}
}
public ICommand ToggleBtnClickCommand { get; set; }
}
And the MainPageViewModel:
public class MainPageViewModel : ViewModelBase
{
public ObservableCollection<ButtonClass> ButtonCollection;
public MainPageViewModel()
{
ButtonCollection = new ObservableCollection<ButtonClass>();
for (int i = 0; i < 20; i++)
{
ButtonCollection.Add(new ButtonClass
{
Label = "TButton " + i,
BChecked = true,
BEnabled = true,
ToggleBtnClickCommand = new DelegateCommand<string>(ToggleBtnClicked)
});
}
}
public void ToggleBtnClicked(string obj)
{
var buttonLable = obj;
}
}
In case you need to check my sample, you can find my demo here.
There are a few concepts that are missing from your code that you need to properly implement for this to work
INotifyPropertyChanged -> Your ButtonClass class acts as a view model for your view. As such, for values to properly update UI and the other way around you need your class to implement it and all your properties to raise the PropertyChanged event when their values change (in the setter)
2-Way bindings -> If you want your ButtonClass instance to update when you click the button and you want your button to change state if you change the property in the ButtonClass, your bindings need to be Mode=TwoWay. You might want to explore the new Bindings {x:Bind} for better performance
Commands -> To bind the click event, you need to use the Command property. You would need to create a ICommand implementation, and create a property in your ButtonClass. The use Command property of the Button to bind to that Command.
Essentially, everything I mentioned are components of an MVVM framework, of which there are many out there. Here are some of the more popular ones: Caliburn, MVVMCross, Prism.
There's also a great starter kit for Windows 10 that will definitely kick start your app and contains a lot of this classes already built - Template10
I have this in my class and I am trying to access it on my web page.
public String carImageURL = "/resources/images/sample_car.jpg";
This is the code I use in my web page that does not work.
<h:graphicImage value="#{carClass.carImageURL}"></h:graphicImage >
When I run the page there is no picture of the car in it.
Change the carImageURL attribute to private scope and write a getter method for it. Also write a slash at the beginning of the image path:
private String carImageURL = "/resources/images/sample_car.jpg";
public String getCarImageURL() {
return carImageURL;
}
I have a problem when dynamically instantiating a PF 3.4.2 AutoComplete component.
The component intially renders ok, its value is refreshed on partial processing
but the suggestions are never displayed.
I am instantiating this control the following way :
AutoComplete ac = (AutoComplete) context.getApplication().createComponent(AutoComplete.COMPONENT_TYPE);
final String varName = "p";
ValueExpression ve = JSFUtils.createValueExpression("#{minContext.selected.sen}"), Sen.Type);
ac.setValueExpression("value", ve);
ac.setForceSelection(true);
ac.setVar(varName);
ValueExpression itemLabel = JSFUtils.createValueExpression("#{sc:senLibelle(p)}"), String.class);
ac.setValueExpression("itemLabel", itemLabel);
ValueExpression itemValue = JSFUtils.createValueExpression("#{" + varName + "}");
ac.setValueExpression("itemValue", itemValue);
MethodExpression completeMethod = JSFUtils.createMethodExpression("#{senUtils.completeAllSens}", List.class,new Class[]{String.class});
ac.setCompleteMethod(completeMethod);
then adding it to parent control using
getChildrens().add(ac);
The parent component is a derivation of PF PanelGrid. I use this approach successfully to generate various edition panels and it works like a charm. But I can not figure why it does not with autoComplete.
The parent control looks like :
#FacesComponent(SenatDataTableEntryDetail.SENAT_COMPONENT_TYPE)
public class SenatDataTableEntryDetail extends PanelGrid {
/** Leaving renderer unchanged, so that PF renderer for PanelGrid is used.
*/
public static final String SENAT_COMPONENT_FAMILY = "fr.senat.faces.components";
public static final String SENAT_COMPONENT_TYPE = SENAT_COMPONENT_FAMILY + ".SenatDataTableEntryDetail";
private enum PropertyKeys { mapper, bean; }
#Override
public void encodeBegin(FacesContext context) throws IOException {
super.encodeBegin(context);
addDynamicChildren(context);
}
#Override
public boolean getRendersChildren()
{
return true;
}
...
private Boolean isInitialized() {
return (Boolean)getStateHelper().eval(SENAT_INITIALIZED,false);
}
private void setInitialized(Boolean param) {
getStateHelper().put(SENAT_INITIALIZED, param);
}
private void addDynamicChildren(FacesContext context) throws IOException {
if(isInitialized()) {
return;
}
setInitialized(true);
/* components are instiated and added as children only once */
}
}
It just adds children to the panel grid.
The other aspects of custom component declaration (in taglib and so on) are ok.
The problem doest not seem to be in EL expressions, completeMethod definition, etc. If I include in my test xhtml page an instanciation of the p:autoComplete with the very same parameters, it just works as expected :
<p:autoComplete value="#{minContext.selected.sen}" forceSelection="true"
var="p" itemLabel="#{sc:senLibelle(p)}" itemValue="#{p}"
completeMethod="#{senUtils.completeAllSens}"/>
I noticed that the PF AutoComplete component is a bit special as it renders differently
when a query is detected. See AutoCompleteRenderer source code in http://primefaces.googlecode.com/files/primefaces-3.4.2.zip .
In the "dynamically instantiated" case, the decode method of this component is not called. I failed to find why those last days, but did not succeed.
I look forward for your suggestions on what to check to correct this annoying "bug".
So, the problem was in id generation (see the two comments).
The beginning of component instantiation becomes :
AutoComplete ac = (AutoComplete) context.getApplication().createComponent(AutoComplete.COMPONENT_TYPE);
ac.setParent(this);
ac.setId(...some application specific unique id generation...);
final String varName = "p";
This way, the naming container is properly taken in account on client id generation.