How to flip Camera used by ZXing in Xamaring Forms - c#

As part of the app i'm developing i want the ability to change between using the front or back camera, but from my searches and attempts i haven't been able to get it to work using the front camera.
The scanner view doing the scanning is the one from ZXing.Net.Mobile.Forms called ZXingScannerView, defined in my xaml like so, together with the button that should do the flipping of the camera.
<elements:AdvancedTabbedPage
...
xmlns:elements="clr-namespace:Wolf.Utility.Main.Xamarin.Elements;assembly=Wolf.Utility.Main"
xmlns:forms="clr-namespace:ZXing.Net.Mobile.Forms;assembly=ZXing.Net.Mobile.Forms">
...
<ContentPage>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{x:Static resources:AppResources.CameraFlipText}" x:Name="CameraFlipButton" Clicked="CameraFlipButton_OnClicked"/>
</ContentPage.ToolbarItems>
<ContentPage.Content>
...
<forms:ZXingScannerView x:Name="ScannerView" HeightRequest="200" IsAnalyzing="False" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" IsVisible="False" IsScanning="True"/>
...
</ContentPage.Content>
</ContentPage>
The Button can be seen in the top right of the following image, while the Scanner View is only visible while scanning is On, which it is not on the image.
Image of the Page where Scannning is happening
Clicking the button should toggle between using the front and back camera, with the front one as the default. Clicking the button however, doesn't seem to do anything, other then write to my Log. Code for the Clicked event of the button can be seen below.
...
private void CameraFlipButton_OnClicked(object sender, EventArgs e)
{
Logging.Log(LogType.Information, "Flipping Camera...");
Config.DefaultOptions.UseFrontCameraIfAvailable = !Config.DefaultOptions.UseFrontCameraIfAvailable;
Config.CustomOptions.UseFrontCameraIfAvailable = !Config.CustomOptions.UseFrontCameraIfAvailable;
if (!ScanningToggle.IsToggled) return;
Logging.Log(LogType.Information, "Restarting Scanning...");
ScanningToggle.IsToggled = false;
ScanningToggle.IsToggled = true;
}
The options mentioned in the above code is defined as so, in my Config class. Additional values in the one called CustomOptions are set in my Init method of my Config class, but those are irrelevant to this question.
public class Config
{
...
public static MobileBarcodeScanningOptions CustomOptions = new MobileBarcodeScanningOptions() { UseFrontCameraIfAvailable = true };
public static MobileBarcodeScanningOptions DefaultOptions = new MobileBarcodeScanningOptions() { UseFrontCameraIfAvailable = true };
...
}
The options that my scanner will use, is always picked between these two, depending on a few user inputs in the settings.
Attempting to get it to work i have also tried to...
Invert the value UseFrontCameraIfAvailable, while scanning is running
Invert the value UseFrontCameraIfAvailable on the options used to start the scan with and then restarting the scan - The code shown above.
Change IsScanning of the ZXingScannerView from true to false, while restarting the scanning with changed options, but this just resulted in the camera freezing.
Found this one as i was about to submit the question. I'm going to attempt to follow that one tomorrow, but would still very much like input on mine.
Fell free to ask questions, or ask for additional code if i have left something out, that you think could help.

I managed to figure out how to successfully flip the camera.
To do so i first remove the ZXingScannerView from my stack that contains it.
Then i create a new instance of the ZXingScannerView, copying over all the settings from the old one (layout positioning, ZXingScannerView specific values and so on).
Then i re-add the ZXingScannerView to the stack, and from there any changes to the UseFrontCameraIfAvailable property took effect.
The code it made to succeed, is as follows. First the generic method that copies over properties, then the method that recreates the ZXingScannerView, and lastly my method that enables the scanning.
public class GenericFactory
{
// Assistance with Setter Accessibility: https://stackoverflow.com/questions/3762456/how-to-check-if-property-setter-is-public
public static T CopyProperties<T>(T newObject, T oldObject, bool ignoreDefaults = true,
bool skipSelectedProperties = true, params string[] skippedProperties) where T : class
{
var type = typeof(T);
var properties = type.GetProperties();
foreach (var property in properties)
{
if (ignoreDefaults && property.GetValue(oldObject) == default)
continue;
if (skipSelectedProperties && skippedProperties.Contains(property.Name))
continue;
if (!property.CanWrite)
continue;
property.SetValue(newObject, property.GetValue(oldObject));
}
return newObject;
}
}
private void RecreateScannerView()
{
if (Config.DebugMode) Logging.Log(LogType.Debug, $"{nam1eof(RecreateScannerView)} method called");
ScannerStack.Children.Remove(ScannerView);
if (Config.DebugMode)
Logging.Log(LogType.Debug,
$"Coping properties from existing {nameof(ZXingScannerView)} into a new {nameof(ZXingScannerView)}");
ScannerView = GenericFactory.CopyProperties(new ZXingScannerView() {IsScanning = false}, ScannerView,
skippedProperties: new List<string>() {nameof(ScannerView.IsScanning)}.ToArray());
ScannerView.OnScanResult += ScannerView_OnScanResult;
ScannerStack.Children.Add(ScannerView);
}
private void EnableScan(MobileBarcodeScanningOptions imputedOptions = null)
{
if (Config.DebugMode) Logging.Log(LogType.Debug, $"{nameof(EnableScan)} Method is run in Thread named => {Thread.CurrentThread.Name}");
var chosenOptions = imputedOptions ?? (Config.UseCustomOptions ? Config.CustomOptions : Config.DefaultOptions);
if (Config.DebugMode)
Logging.Log(LogType.Information,
$"Chose this option for Scanning => {(imputedOptions != null ? nameof(imputedOptions) : (Config.UseCustomOptions ? nameof(Config.CustomOptions) : nameof(Config.DefaultOptions)))}");
ScannerView.Options = chosenOptions;
RecreateScannerView();
Logging.Log(LogType.Information, $"Starting the Scanning...");
ScannerView.IsScanning = true;
ScannerView.IsAnalyzing = true;
ScannerView.IsVisible = true;
if (Config.DebugMode)
Logging.Log(LogType.Debug,
$"{nameof(EnableScan)} Called and Finished; ScannerView.IsAnalyzing => {ScannerView.IsAnalyzing}; ScannerView.IsVisible => {ScannerView.IsVisible}");
}
My method to flip the value of UseFrontCameraIfAvailable is the one shown in the question above.
Hope to this ends up helping others who might stumble upon a likewise issue.

I don't think it can switch the front and back cameras when it has started scanning with Zxing,so the option has to be chosen and set beforehand
var options = new MobileBarcodeScanningOptions
{
AutoRotate = true,
UseNativeScanning = true,
TryHarder = true,
TryInverted = true,
UseFrontCameraIfAvailable = true
};
var scannedCode = await _scanner.Scan(options);

Related

Why is my AutoComplete suggestion dropdown blank

I have a Xamarin form where I am trying to add a SyncFusion AutoComplete control. The data is a simple class with only three string fields (CUSTNMBR, CUSTNAME, ZIP). I want it to match on any of the fields and display the coresponding CUSTNMBR. Here it my line in Xaml:
<xForms:SfAutoComplete x:Name="customerAutoComplete" WidthRequest="120" BackgroundColor="White" />
In the form's code-behind constructor I call LoadCustomerData():
private async void LoadCustomerData()
{
customerAutoComplete.DataSource = await GetCustomerCodes();
customerAutoComplete.DisplayMemberPath = "CUSTNMBR";
customerAutoComplete.SelectedValuePath = "CUSTNMBR";
customerAutoComplete.SuggestionMode = SuggestionMode.Custom;
customerAutoComplete.Filter = FilterCustomers;
customerAutoComplete.AutoCompleteMode = AutoCompleteMode.Suggest;
customerAutoComplete.Watermark = "Zip Code, Customer ID, or Customer Name";
customerAutoComplete.MinimumPrefixCharacters = 3;
}
Here is my filter method.
private bool FilterCustomers(string search, object customer)
{
var text = customerAutoComplete.Text;
if (customer != null)
{
var myCustomer = (OrganizationSearchDto)customer;
if (myCustomer.CustName.Contains(text) || myCustomer.CustNmbr.Contains(text) ||
myCustomer.Zip.Contains(text))
{
return true;
}
}
return false;
}
The above code worked partially when I had customerAutoComplete.SuggestionMode = SuggestionMode.Contains but it did not match on the other two fields. Now it still runs, however nothing is shown in the dropdown list (its blank). Why is my dropdown blank? Any hints, suggestion or a hard shove in the right direction will be appreciated.
For anyone encountering this, tests to try:
Put a breakpoint on return true - is that breakpoint hit for the customer(s) you expect to be shown as suggestions?
Swap return true and return false, so it is true for all the OTHER customers - the opposite of what you want. See if it is still blank. If it is, then it isn't the filter - code elsewhere is interfering with display. Would need to show more code, or make a github containing a minimum repo that shows the problem.
[from OP] The issue was that property names on DisplayMemberPath are case sensitive, as are the filter checks.
The fix for the filter was to ignore case everywhere. E.g.
if (myCustomer.CustName.ToLower().Contains(text.ToLower()) || ...)
We have analyzed the reported query. We have achieved the requirement by using the following code snippet,
public bool ContainingSpaceFilter(string search, object item)
{
if (item != null)
{
var myCustomer = item as Employee;
if (**myCustomer.Name.ToUpper().Contains(search.ToUpper()**) || myCustomer.ID.Contains(search) ||
myCustomer.ZipCode.Contains(search))
{
return true;
}
}
return false;
}

In WPF, how do I change the source of an image programatically? [duplicate]

This question already has answers here:
How to load image to WPF in runtime?
(2 answers)
Closed 1 year ago.
I have a custom control with a default image that I want to change based on which iteration of the control it is. For example, I have one for "F1" and "NumLock" and so on. In the constructor of the control, I have this:
public FixerBox(Dictionary<string,string> deets)
{
InitializeComponent();
btnOff();
this.FixerTitle.Text = deets["Title"];
this.FixerDesc.Text = deets["Description"];
this.FixerTags.Text = deets["Tags"];
this.FixerImg.Source = new BitmapImage(new Uri(deets["Img"], UriKind.Relative));
}
The bitmap stuff was based on another answer and produces this:
Below is the control itself showing that it's correctly getting the title, tags, and description, but the image is bunk (on the left side, that thin grey line is the border that should be around the image).c#
If I was using HTML/CSS, I could right-click the image to see what exactly its properties are, but I don't know how to get that kind of information using WPF. The best I could manage was in the top area is a status window where I've manually printed a "Tostring" output of the first controls image source data. Near as I can tell, it's all correct, but there's no actual image there. Every subsequent control has the same output (one thin line where the image should be).
EDIT Per comments, here is some more of the information. The main XAML file loads up the controls like so in its constructor:
public partial class MainWindow : Window
{
private Fixers fixers = new Fixers();
// This is the custom control consisting mostly of various boxes
private Dictionary<string,FixerBox> fixerBoxes = new Dictionary<string, FixerBox> { };
public MainWindow()
{
InitializeComponent();
var fixNames = fixers.FixerNames();
foreach (string key in fixNames)
{
fixerBoxes[key] = new FixerBox(fixers.GetFix(key));
FixersArea.Children.Add(fixerBoxes[key]);
}
StatusBox.Text += fixerBoxes["F1"].FixerImg.Source.ToString();
}
}
The fixers variable is of class Fixers which consists of the below (abbreviated to show just the F1 function for brevity):
class Fixers
{
private string ClearWS(string str)
{
var first = str.Replace(System.Environment.NewLine, "");
return first.Replace("\t", "");
}
// Loads registry functions
private Regis regStuff = new Regis();
// Loads preferences from the file
private Prefs prefs = new Prefs();
// A timer to make sure the system behaves
private Timer watcher;
// Watcher action toggles
private bool watchNumL = false;
// Translation array from fix shortname to various data about them
private Dictionary<string, Dictionary<string, string>> fixers = new Dictionary<string, Dictionary<string, string>>
{
["F1"] = new Dictionary<string,string> {
["PrefName"] = "KillF1UnhelpfulHelp",
["Img"] = #"/graphics/F1key.png",
["Title"] = #"Diable F1 ""Help"" function",
["Description"] = #"
Have you ever hit the F1 key by accident and had a distracting and unhelpful window or webpage open as a result?
Windows set the F1 key to a generic help function that basically never helps and always gets in the way.
Enable this control to disable that obnoxious design choice. Note that some programs still respond to F1 on their own accord,
but this will stop the default Windows behavior in things like Windows Explorer at least.
",
["Tags"] = "#Keyboard,#Rage"
},
};
public Fixers()
{
// The readability hack above with multi-line strings introduces a bunch of extra whitespace. Let's clear that out
foreach (var fixKey in fixers.Keys)
{
fixers[fixKey]["Description"] = ClearWS(fixers[fixKey]["Description"]);
}
}
public List<string> FixerNames()
{
return fixers.Keys.ToList();
}
public bool IsFixed(string which)
{
// If we're watching, it's fixed
if ("NumL" == which) return watchNumL;
// For anything registry related
return regStuff.IsFixed(which);
}
public Dictionary<string,string> GetFix(string which)
{
return fixers[which];
}
}
if you use binding, you can create in your ViewModel a string, in which is stored the path of your image, then you can easily change programatically its path.
Then in XAML just bind image's source to the string.
In my case I have a list of objects, with the property `ImageName' :
<Image Source="{Binding DataContext.SelectedMacro.ImageName,
RelativeSource={RelativeSource AncestorType=Window}}"/>

C# Dynamic form (reflection) - linking controls

Sorry for the poor quality of the title. I couldn't think of a better way to phrase this.
For a project I'm currently working on with a few friends, I got myself in the situation where I have created a dynamic form (with reflection) which I now want to validate.
Example (ignore the black box, it contains old form elements which are now irrelevant and i didn't want to confuse you guys):
As you may have guessed already, it is an application for creating a mysql database.
Which is where I get to my problem(s). I want to disable checkboxes if others are checked.
For example: If I check "PrimaryKey" I want to disable the checkbox "Null".
Changing from unsigned to signed changes the numericupdown minimum and maximum etc.
But with reflection and all, I find it difficult to know exactly which checkbox to disable.
I was hoping you guys would have some suggestions.
I have been thinking about this for a while and a few thoughts have come to mind. Maybe these are better solutions than the current one.
Thought 1: I create UserControls for every datatype. Pro's: no problems with reflection and easy identifying of every control in the UserControl for validation. Con's: Copy-Pasting, Lots of UserControls, with a lot of the same controls.
Thought 2: Doing something with the description tags for every property of the classes. Creating rules in the description that allow me to link the checkboxes together. Here I'll only have to copy the rules to every class property and then it should be ok.
I had been thinking of other solutions but I failed to remember them.
I hope you guys can give me a few good pointers/suggestions.
[Edit]
Maybe my code can explain a bit more.
My code:
PropertyInfo[] properties = DataTypes.DataTypes.GetTypeFromString(modelElement.DataType.ToString()).GetType().GetProperties();
foreach (PropertyInfo prop in properties)
{
if (prop.Name != "Label" && prop.Name != "Project" && prop.Name != "Panel")
{
var value = prop.GetValue(modelElement.DataType, null);
if (value != null)
{
tableLayoutPanel1.Controls.Add(new Label { Text = prop.Name, Anchor = AnchorStyles.Left, AutoSize = true });
switch (value.GetType().ToString())
{
case "System.Int32":
NumericUpDown numericUpDown = new NumericUpDown();
numericUpDown.Text = value.ToString();
numericUpDown.Dock = DockStyle.None;
tableLayoutPanel1.Controls.Add(numericUpDown);
break;
case "System.Boolean":
CheckBox checkBox = new CheckBox();
checkBox.Dock = DockStyle.None;
// checkbox will become huge if not for these changes
checkBox.AutoSize = false;
checkBox.Size = new Size(16, 16);
if (value.Equals(true))
{
checkBox.CheckState = CheckState.Checked;
}
tableLayoutPanel1.Controls.Add(checkBox);
break;
default:
MessageBox.Show(#"The following type has not been implemented yet: " + value.GetType());
break;
}
}
}
}
Here is a mockup from my comments:
// The ViewModel is responsible for handling the actual visual layout of the form.
public class ViewModel {
// Fire this when your ViewModel changes
public event EventHandler WindowUpdated;
public Boolean IsIsNullCheckBoxVisible { get; private set; }
// This method would contain the actual logic for handling window changes.
public void CalculateFormLayout() {
Boolean someLogic = true;
// If the logic is true, set the isNullCheckbox to true
if (someLogic) {
IsIsNullCheckBoxVisible = true;
}
// Inform the UI to update
UpdateVisual();
}
// This fires the 'WindowUpdated' event.
public void UpdateVisual() {
if (WindowUpdated != null) {
WindowUpdated(this, new EventArgs());
}
}
}
public class TheUI : Form {
// Attach to the viewModel;
ViewModel myViewModel = new ViewModel();
CheckBox isNullCheckBox = new CheckBox();
public TheUI() {
this.myViewModel.WindowUpdated += myViewModel_WindowUpdated;
}
void myViewModel_WindowUpdated(object sender, EventArgs e) {
// Update the view here.
// Notie that all we do in the UI is to update the visual based on the
// results from the ViewModel;
this.isNullCheckBox.Visible = myViewModel.IsIsNullCheckBoxVisible;
}
}
The basic idea here is that you ensure that the UI does as little as possible. It's role should just be to update. Update what? That's for the ViewModel class to decide. We perform all of the updating logic in the ViewModel class, and then when the updating computations are done, we call the UpdateVisual() event, which tells the UI that it needs to represent itself. When the WindowUpdated Event occurs, the UI just responds by displaying the configuration set up by the ViewModel.
This may seem like a lot of work to set up initially, but once in place it will save you tons and tons of time down the road. Let me know if you have any questions.
Try relating the event of one checkbox to disable the other; something like this:
private void primaryKeyBox_AfterCheck(object sender, EventArgs e)
{
nullBox.Enabled = false;
}
This is a very simple example and would have to be changed a bit, but for what I think you're asking it should work. You would also have to add to an event for the boxes being unchecked. You would also need logic to only get data from certain checkboxes based on the ones that are and are not checked.
For all the other things, such as changing the numbers based on the dropdown, change them based on events as well.
For WinForms I would use data binding.
Create an object and implement INotifyPropertyChanged and work with that object.
Then, If you have an object instance aObj:
To bind the last name property to a textbox on the form do this:
Private WithEvents txtLastNameBinding As Binding
txtLastNameBinding = New Binding("Text", aObj, "LastName", True, DataSourceUpdateMode.OnValidation, "")
txtLastName.DataBindings.Add(txtLastNameBinding)
Take a look here for more info.
INotifyPropertyChanged

Is it possible in MonoTouch to determine if the user clicked on an image in a BadgeElement?

I have amended the TODO list app to use a badge element instead of the boolean element as follows:
protected void PopulateTable()
{
tasks = TaskManager.GetTasks().ToList ();
UIImage ticked = new UIImage("checkbox_checked.png");
UIImage unticked = UIImage.FromFile("checkbox_unchecked.png");
Root = new RootElement("Tasky") {
new Section() {
from t in tasks
select (Element) new BadgeElement(t.Completed ? ticked : unticked, (t.Name==""?"<new task>":t.Name), delegate {
Console.WriteLine("???");
})
}
};
}
Is it possible to check to see if the user has clicked an icon rather than the text, and change the behaviour? Essentially I want to do this...
var task = tasks[indexPath.Row];
if(clickedIcon) {
currentTask = task;
task.Completed = !task.Completed;
TaskManager.SaveTask(currentTask);
} else {
ShowTaskDetails(task);
}
But I don't see any parameters inside IndexPath that allow me to access the column or the tapped element.
Any ideas
You need to create a custom version of the BadgeElement, and basically raise an event for the image that is separate from raising an event for the text.
Luckily for you, the whole source code is available, so you can just copy/paste BadgeElement, rename it, create a new unique key and modify it.

MonoTouch Dialog - OnTap Not Working

I am using the Reflection API for MonoTouch.Dialog. What I want to accomplish is, when the user selects an item from a list, I want the navigation controller to go back. I don't want to force the user to click an item, then click the Back button to go back.
However, when trying to use the OnTap attribute, my method doesn't get executed.
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
var demo = new DemoClass();
var context = new BindingContext(this, demo, "Some Demo");
var controller = new DialogViewController(context.Root);
var navController = new UINavigationController(controller);
window.RootViewController = navController;
window.MakeKeyAndVisible ();
return true;
}
public void EnumSelected()
{
InvokeOnMainThread(() =>
{
new UIAlertView("Dialog", "Enum Selected", null, "OK", null).Show();
});
}
DemoClass
public enum DemoEnum
{
SomeValue,
AnotherValue,
YetAnotherValue
}
public class DemoClass
{
[OnTap("EnumSelected")]
public DemoEnum SomeEnum;
}
I know how to navigate back with the navigation controller, but without the OnTap working, I can't get that far. Am I missing something? Can anybody see where I am going wrong?
In a word, you can't.
Enum's (which results in a new RootController and a bunch of RadioElement's) can't have an OnTap set, unless you do it all by hand.
https://github.com/migueldeicaza/MonoTouch.Dialog/blob/master/MonoTouch.Dialog/Reflect.cs#L337
especially, these bits:
csection.Add (new RadioElement (ca != null ? ca.Caption : MakeCaption (fi.Name)));
element = new RootElement (caption, new RadioGroup (null, selected)) { csection };
There is no trigger added to the RadioElement. You would need to change it to auto-pop the form - which needs a new/changed RadioElement
https://gist.github.com/3569920
(I can't claim this code - it came from #escoz: https://github.com/escoz/MonoMobile.Forms )
So, if you are using the built-in MT.D, you can't do it. If you don't mind maintaining your own branch (or, submit a pull request back, which is what I need to do for a few things), then this is a fairly good way to go.

Categories