I had a previous question regarding a way to access a proxy card from within a web page when using Chrome. I'm now attempting to instead use a vb.Net application with an embedded CefSharp object. I have the code I need to access the proxy card (thanks to Smart Card API), but I need an easy way to indicate that this is even an option. My thought is to:
Put an otherwise empty element on the web page (such as <div id='smartcard' />)
Inside Visual Basic, monitor the contents of the page for this <div />
If the <div /> is found, make sure the card reader is detected. If so, add some text (and maybe an image) to its contents indicating the the card can be scanned
Once a card scan is detected, put the value from the card into a form element and POST it
It seems likely to me that I'm going to have to use some combination of JavaScript and vb.net code, but I'm so new to CefSharp that I really have no idea where to start.
Thanks in advance for all your help.
Not being a C# programmer, I looked at the information on the General Usage guide many times and still didn't really understand it. That said, I think I've been able to get this project off the ground. In addition to the CefSharp project, I'm also using the non-free Smart Card API from CardWerk.
Below is some snippets of what I did.
VB.Net
Imports CefSharp
Imports Subsembly ' For the SmartCard namespace
Class MainWindow
Private WithEvents CardManager As SmartCard.CardTerminalManager
Private Sub MainWindow_Initialized(sender As Object, e As EventArgs) Handles Me.Initialized
browser.Address = "https://jake-dev7.local/trainingmatrix/"
Debug.Print(SmartCard.SMARTCARDAPI.API_VERSION)
CardManager = SmartCard.CardTerminalManager.Singleton
CardManager.Startup(True)
End Sub
Private Sub browser_LoadingStateChanged(sender As Object, e As LoadingStateChangedEventArgs) Handles browser.LoadingStateChanged
Dim script As String
If Not e.IsLoading Then
If CardManager.SlotCount Then
script = "if ($('#proxcard').length) { proxcard_init() }"
browser.GetMainFrame().ExecuteJavaScriptAsync(script)
End If
End If
End Sub
Protected Sub InsertedEvent(ByVal aSender As Object, ByVal aEventArgs As SmartCard.CardTerminalEventArgs) Handles CardManager.CardInsertedEvent
Dim aCard As SmartCard.CardHandle
Dim nActivationResult As SmartCard.CardActivationResult
Dim iFacilityCode As Integer
Dim iCardID As Integer
' There's a bunch of code here taken from the sample code that came
' with the SmartCard API from CardWerk to pull the facility code and
' card id out of the prox card.
If iFacilityCode <> 0 And iCardID <> 0 Then
Dim script As String
script = "if ($('#proxcard').length) { proxcard_scan(" & iFacilityCode & ", " & iCardID & ") }"
browser.GetMainFrame().ExecuteJavaScriptAsync(script)
End If
End Sub
End Class
JavaScript
(This is in a .js file that is loaded by the web page. This page can also be loaded in Chrome, Firefox, IE, etc and these functions will never be run which keeps this utility usable for computers that don't have the custom .exe and card reader).
// These proxcard_* functions are called via our parent application
// (CefSharp object embeded in a vb.Net assembly)
function proxcard_init() {
$('#proxcard').html("<div class='or'>- OR -</div><div><img src='proxcard.jpg'><br>Scan your card</div>");
}
function proxcard_scan(facilityID, cardID) {
var vars = {
facilityID: facilityID,
cardID: cardID
};
if ($('form#adduser').length) {
// We're on the add user page. Check to see if this card matches somebody.
$.post('httprequest.php?type=get-emp-from-prox', vars, function(data) {
if (data && data.number) {
// Update UI and backend form fields. If everything validates, submit the form
} else {
// Clear UI and backend form fields that pertain to user ID
alert('Card not found');
}
}, 'json');
} else if ($('form#update').length) {
// Deal with the update form
}
}
In my actual code, I have multiple else if statements for dealing with different forms where I allow a card to be scanned. They are not included to keep this from getting out of hand :).
Please Note: This is not intended to be the entire project or all the code needed to process prox cards using CefSharp. My hope is that it will be enough to help somebody else.
Related
I am using in a WinForm an object of type:
CefSharp.WinForms.ChromiumWebBrowser
Everything is working fine but I am having an issue when I try to change the ZoomLevel with SetZoomLevel method:
If oBrowser.IsBrowserInitialized Then
oBrowser.SetZoomLevel(-2.0)
Dim frame As CefSharp.IFrame = oBrowser.GetMainFrame
Dim request As CefSharp.IRequest = frame.CreateRequest()
request.Url = url
request.Method = "POST"
request.InitializePostData()
Dim element = request.PostData.CreatePostDataElement()
element.Bytes = postDataBytes
request.PostData.AddElement(element)
request.Headers = headers
frame.LoadRequest(request)
End If
The first time I open the WinForm the Zoom level doesn't change, while it works correctly from the 2nd refresh.
Am I missing some initialization and/or method call... Or do I have to call this method in another position?
Note: the CEFSharp DLL version is 63.0.3.0.
The .NET Framework is 4.5.2
EDITED 01.06.2018: I've found a solution (see below) but now there's another problem: the zoom change is made when the browser is already visible, so it's not nice for the final user to see the page size changing during the form load.
Has anyone a suggestion to freeze the layout during zoom change? Please note that .SuspendLayout() and .ResumeLayout() are not working.
I've found a way to answer my own question. I post it here as it could be useful to other users.
You must add a LoadingStateChanged handler to the ChromiumWebBrowser object:
AddHandler oBrowser.LoadingStateChanged, AddressOf WebBrowserOnLoadingStateChanged
The method would be then something like:
Private Sub WebBrowserOnLoadingStateChanged(ByVal sender As Object, ByVal loadingStateChangedEventArgs As LoadingStateChangedEventArgs)
Dim oBrowser As WinForms.ChromiumWebBrowser = CType(sender, WinForms.ChromiumWebBrowser)
If Not oBrowser.IsLoading Then
oBrowser.SetZoomLevel(-2.0)
End If
End Sub
This solution works good in my environment but now there's another problem: the zoom change is made when the browser is already visible, so it's not nice for the final user to see the Zoom Level changing during the form load.
I have very little experience of working with VB.NET Web Application.
I would like to know how to capture function keys (F1, F2... F12) in a "VB WebForms applications (Web Application)".
I have been searching on search engines and even here on stackoverflow but I kept getting results full of VB.NET Windows Forms which showed examples of _keydown event like
Private Sub xyz_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
I tried looking into my WebForm's different event like Form's event, TextBox's event etc. but couldn't find it anywhere. So I assume these are available for Windows Forms ONLY.
What I am trying to do is as follows -
In a TextBox when the user presses F1 key, open a new form which has a Gridview with some data
The user clicks on one row and the value(s) (one or more) of this row is captured in variable(s) and the form should close and control should come back to TextBox with the captured row data.
Also how can I setFocus on any particular row of TextBox?
Can someone please give me a small example of how this can be done.
You can't capture it on the server side, but maybe you could try this:
<script type="text/javascript">
$(document).on("keypress", function (e) {
var code = e.which;
if (code == 123) { // The F12 Key
$.ajax({
type: "POST",
url: "YourCallingPage.aspx/OpenForm",
contentType: "application/json; charset=utf-8",
dataType: "json"
});
}
});
</script>
Then in the codebehind of YourCallingPage.aspx(what ever page the textbox is on) add the following method(or whatever you name the method):
<System.Web.Services.WebMethod()> _
Public Shared Sub OpenForm()
HttpContext.Current.Response.Redirect("FormThatHasGrid.aspx")
End Sub
Now once you redirect back to YourCallingPage.aspx I guess you could append the selected grid value to the query string and populate the textbox like that, or some other method, but this is just a quick and dirty idea that may work for you.
The medical company I work for has a EMR system setup to keep digital copies of patient files so they are searchable as well as quick to access. A new request has come through to be able to save e-mail to the EMR system, however it does not display .msg files very nicely. It does display files nicely as .htm, so was hoping i could figure out a way to save email messages to a specific folder in a .htm format with the user just hitting a single button.
Should i be looking at making an add-in using vs 2010 to do this simple task? Or would there be a better way to do this?
I've explored making an Add-In breifly over the past few days using command bars but have hit numerous problems with adding the menu item to mail items, as well as losing event handlers or having them fire quite a few times, so i'm wondering if i'm barking up the wrong tree.
Edit: Looking at ribbon bar customization as well, may have to upgrade some users that are still using 2003, but seems like it might be the better option than command bars going forward.
Ribbon bar was the best path i found, however i had trouble finding a great how-to for the start-to-finish project, so i'll make a small write up here.
To add a button to the ribbon for only existing mail messages including a image for the button.
Using VS 2010
New project, Office, select "Outlook 2007 add in", enter a name for your project.
To your newly created project, Add a new item "Ribbon (XML)" name it however you want, i'll call it CustomRibbon
open your newly created CustomRibbon.xml file and change the tab node to have the following
<tab idMso="TabReadMessage">
<group insertBeforeMso="GroupActions" id="CustomGroup" label="GroupNameThatShowsInOutlook">
<button id="btnCustomButton"
label = "Text For The Custom Button"
supertip="tip for the button hover"
onAction ="ButtonClicked"
size="large"
getImage="GetCustomButtonImage" />
</group>
</tab>
This then has 2 callback functions to the CustomRibbon.cs file, one called GetCustomButtonImage, the other ButtonClicked.
open CustomRibbon.cs to fill this in, in the Ribbon Callbacks region add the following
public void ButtonClicked(Office.IRibbonControl Control)
{
//Do work here
}
also add the following in the same section
public stdole.IPictureDisp GetCustomButtonImage(Office.IRibbonControl control)
{
System.Drawing.Image myImage;
myImage = OutlookAddIn.Properties.Resources.ImageName;
return AxHostConverter.ImageToPictureDisp(myImage);
}
this will then show there is a class missing, we'll get to that shortly, but first we are going to add in the last part we need in CustomRibbon.cs. In the IRibbonExtensibility Members region, in GetCustomUI change the existing code
public string GetCustomUI(string ribbonID)
{
if (ribbonID == "Microsoft.Outlook.Mail.Read")
{
return GetResourceText("OutlookAddIn.CustomRibbon.xml");
}
else
{
return "";
}
}
Add a new class to your project call it AxHostConverter, add add this to the top
using System.Windows.Forms;
using System.Drawing;
Then change the class to have the following code
class AxHostConverter : AxHost
{
private AxHostConverter() : base("") { }
static public stdole.IPictureDisp ImageToPictureDisp(Image image)
{
return (stdole.IPictureDisp)GetIPictureDispFromPicture(image);
}
static public Image PictureDispToImage(stdole.IPictureDisp pictureDisp)
{
return GetPictureFromIPicture(pictureDisp);
}
}
Add your image for your button to the project, and change the GetCustomButtonImage function to use that resource. I used a PNG and had good luck with transparencies displaying well.
And finally, all that should be left is to add the following to ThisAddIn.cs
protected override Microsoft.Office.Core.IRibbonExtensibility CreateRibbonExtensibilityObject()
{
return new CustomRibbon();
}
Add whatever code you are wanting to ButtonClicked and you are set.
Deploy using Clickonce and installation is fairly straightforward.
I'm using the DockPanel Suite by Weifen Luo in a little project (webbrowser) and have managed to be able to create tabs and navigate the webbrowser element inside each tab.
But how am I able to change the tabs title/name when the page is navigating to another site?
Basically I just need to get into the current tabs form.
You can get the current tab by using DockPanel's ActiveContent method. For example:
Form myForm = myDockPanel.ActiveContent();
myForm.TabText = "Stack Overflow";
DockPanel.ActiveDocument and DockPanel.ActivePane can also be useful.
After having worked on this a few weeks (not 'till now though :P) I have to say, that this is currently not possible.
You can manage your own (assuming your Document Form is a specific class) by managing:
'FormClosing' and 'Activated' events
'Activated' set your own "active" document to 'this'.
'FormClosing' set your own "active" document to null.
FormClosing is just to catch the case where you are closing the last document. Activated is what manages everything else, like when a new document gets created and is made the active window, etc.
You can use a static global to manage focus. Then access it from anywhere else:
public partial class MyDocument : DockContent
{
public static MyDocument ActiveDocument { get; private set; }
I needed the ability to check which document was active, and set that document to active again after changing some UI elements that automatically reset the active tab, so I used some pieces from here and the DockPanel FAQ, and did some digging to figure out the answer to this problem:
public string GetActive()
{ //Verify if forms that dock in main window are already open
foreach (DockContent form in dockMain.Contents)
{
if (form.DockHandler.Pane.ActiveContent.DockHandler.Form.Name.ToString() == form.Name.ToString())
{
string formName = form.Name.ToString();
return formName;
}
}
return null;
}
And then in some other method you will call:
string activeForm = GetActive();
I've got a VB.NET class that is invoked with a context menu extension in Internet Explorer.
The code has access to the object model of the page, and reading data is not a problem. This is the code of a test function...it changes the status bar text (OK), prints the page HTML (OK), changes the HTML by adding a text and prints again the page HTML (OK, in the second pop-up my added text is in the HTML)
But the Internet Explorer window doesn't show it. Where am I doing wrong?
Public Sub CallingTest(ByRef Source As Object)
Dim D As mshtml.HTMLDocument = Source.document
Source.status = "Working..."
Dim H As String = D.documentElement.innerHTML()
MsgBox(H)
D.documentElement.insertAdjacentText("beforeEnd", "ThisIsATest")
H = D.documentElement.outerHTML()
MsgBox(H)
Source.status = ""
End Sub
The function is called like this from JavaScript:
<script>
var EB = new ActiveXObject("MyObject.MyClass");
EB.CallingTest(external.menuArguments);
</script>
To the best of my understanding, in order to use insertAdjacentText or any of the other editing methods, the document object should be in the design mode.
In design mode you can edit the document freely, and so can the user.
Check this site for more details
I do not think that Alex is right, something else is the matter.
When I tried to do something like that, insertBefore would not work for me, but appendChild worked just fine, so adding an element is possible.
I worked in Javascript, but I don't expect that makes a difference.