I'm creating an application where the user has to complete a series of steps sequentially. Within the controller I check if each step is complete or in progress and then based on this information, I add the style attributes to the ViewModel which are then passed to the view.
Here's the view with the steps:
So in the controller I have the following code that is currently populating the view:
switch (WhatStep)
{
case 1:
ViewModel.WhatStep = 1;
ViewModel.StepOneComplete = false;
ViewModel.StepOnePanelColour = "info";
ViewModel.StepOneStatusText = "<span class=\"text-info pull-right\"><strong>Next Step</strong></span>";
ViewModel.StepOneCollapsedText = "open";
ViewModel.StepTwoComplete = false;
ViewModel.StepTwoPanelColour = "default";
ViewModel.StepTwoStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
ViewModel.StepTwoCollapsedText = "collapsed";
ViewModel.StepThreeComplete = false;
ViewModel.StepThreePanelColour = "default";
ViewModel.StepThreeStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
ViewModel.StepThreeCollapsedText = "collapsed";
ViewModel.StepFourComplete = false;
ViewModel.StepFourPanelColour = "default";
ViewModel.StepFourStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
ViewModel.StepFourCollapsedText = "open";
ViewModel.StepFiveComplete = false;
ViewModel.StepFivePanelColour = "default";
ViewModel.StepFiveStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
ViewModel.StepFiveCollapsedText = "collapsed";
ViewModel.StepSixComplete = false;
ViewModel.StepSixPanelColour = "default";
ViewModel.StepSixStatusText = "<span class=\"text-default pull-right\"><strong>Upcoming</strong></span>";
ViewModel.StepSixCollapsedText = "collapsed";
break;
case 2:
ViewModel.WhatStep = 2;
ViewModel.StepOneComplete = true;
ViewModel.StepOnePanelColour = "success";
ViewModel.StepOneStatusText = "<span class=\"text-success pull-right\"><strong>✔ Complete</strong></span>";
ViewModel.StepOneCollapsedText = "collapsed";
ViewModel.StepTwoComplete = false;
ViewModel.StepTwoPanelColour = "info";
ViewModel.StepTwoStatusText = "<span class=\"text-info pull-right\"><strong>Next Step</strong></span>";
ViewModel.StepTwoCollapsedText = "open";
The controller goes on and on as the viewModel is populated for each step.
The html in view for each panel look like this:
<div class="panel panel-#Model.StepOnePanelColour">
<div class="panel-heading">
<h4 class="panel-title">
<strong>Step One:</strong>
<a data-toggle="collapse" data-parent="#accordion" href="#collapseOne" class="#Model.StepOneCollapsedText"></a>
#Html.Raw(Model.StepOneStatusText)
</h4>
</div>
<div id="collapseOne" class="panel-collapse collapse" style="height: 0px;">
<div class="panel-body">
<p>Instructions here<p>
</div>
</div>
</div>
Although this approach is working fine, the controller contains over 500 lines of code, just to populate the viewModel depending on what step the user is at.
Is there a better approach to doing this?
Not only could you reduce the controller code to just a few lines, you can significantly reduce the view code as well.
Rather than having a view models with multiple properties for each step, use a collection to represent the steps
public enum StepStatus
{
Upcoming,
Current,
Complete
}
public class Step
{
public int Number { get; set; }
public StepStatus Status { get; set; }
}
Then in the method which generates the view
public ActionResult Index(int currentStep)
{
List<Step> model = new List<Step>();
for (int i = 0; i < 10; i++) // generates 10 steps
{
model.Add(new Step() { Number = i + 1 });
}
// Set the status of the steps based on the current step
for (int i = 0; i < currentStep; i++)
{
model[i].Status = StepStatus.Complete;
}
model[currentStep - 1].Status = StepStatus.InProgress;
return View(model);
}
And the view simply uses a loop to generate each step
#model List<Step>
....
<div id="accordion">
#for (int i = 0; i < Model.Count; i++)
{
<div class="panel #Model[i].Status.ToString().ToLower()"> // adds class name based on the status
<div class=" panel-heading">
<h4 class="panel-title">
<strong>Step #Model[i].Number</strong>
#{ string className = Model[i].Status == StepStatus.Current ? "open" : "collapsed"; }
<a data-toggle="collapse" data-parent="#accordion" href="#step#(Model[i].Number)" class="#className"></a>
<span class="text-default pull-right">
#if (Model[i].Status == StepStatus.Complete)
{
<span class="glyphicon glyphicon-ok" aria-hidden="true"></span> // adds the tick mark
}
<strong>#Model[i].Status</strong>
</span>
</h4>
</div>
<div id="step#(Model[i].Number)" class="panel-collapse collapse">
<div class="panel-body">
<p>Instructions here<p>
</div>
</div>
</div>
}
</div>
and add some css to mimic the panel-succcess, panel-infoand panel-default bootstap classes
.complete {
border-color: #d6e9c6;
}
.complete > .panel-heading {
color: #3c763d;
background-color: #dff0d8;
border-color: #d6e9c6;
}
.current {
border-color: #bce8f1;
}
.current > .panel-heading {
color: #31708f;
background-color: #d9edf7;
border-color: #bce8f1;
}
.upcoming {
border-color: #ddd;
}
.upcoming > .panel-heading {
color: #333;
background-color: #f5f5f5;
border-color: #ddd;
}
You have not indicated how your rendering the content within the <div class="panel-body"> element, but this can simply be done using #Html.Action() if you want to include content for all steps, or using ajax to load the content as required. Using some conventions, the controller method could simply be
public PartialViewResult Step(int number, StepStatus status)
{
var model = .... // get the model based on the step number
string viewName = string.Format("_Step{0}{1}", number, status)
return PartialView(viewName, model);
}
where your partials are named _Step1Complete.cshtml, _Step1Current.cshtml etc (I assume you have different content based on the status), and the to generate the content in the view
<div class="panel-body">
#{ Html.RenderAction("Step", new { number = Model[i].Number, status = Model[i].Status }) // assumes the Step() method is in the same controller
</div>
I would suggest to do the styling via javascript or jQuery and CSS. By evaluating your ViewModel. Let the browser do the handling of UI styling which was designed to that.
Related
In my college ecommerce gallery project I want do when the image is uploaded the size of the image automatically compressed like if the image size is 2mb after upload its gonna 600 kb or 400kb so how to do that..my code is below..
ProductController.cs
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Upsert(ProductVM productVM)
{
if (ModelState.IsValid)
{
string webRootPath = _hostEnvironment.WebRootPath; //getting image Path
var files = HttpContext.Request.Form.Files; //Retrive all the files that are uploaded
if(files.Count > 0) //that means file was uploaded
{
string fileName = Guid.NewGuid().ToString();
var uploads = Path.Combine(webRootPath, #"images\products");
var extenstion = Path.GetExtension(files[0].FileName);
if (productVM.Product.ImageUrl != null)
{
//this is an edit and we need to remove old image
var imagePath = Path.Combine(webRootPath,
productVM.Product.ImageUrl.TrimStart('\\'));
if (System.IO.File.Exists(imagePath))
{
System.IO.File.Delete(imagePath);
}
}
using (var filesStreams = new FileStream(Path.Combine(uploads, fileName +
extenstion), FileMode.Create))
{
files[0].CopyTo(filesStreams);
}
productVM.Product.ImageUrl = #"\images\products\" + fileName + extenstion;
}
else
{
//update when they do not change the image
if (productVM.Product.Id != 0)
{
Product objFromDb = await _unitOfWork.Product.GetAsync(productVM.Product.Id);
productVM.Product.ImageUrl = objFromDb.ImageUrl;
}
}
if (productVM.Product.Id == 0)
{
await _unitOfWork.Product.AddAsync(productVM.Product);
}
else
{
_unitOfWork.Product.Update(productVM.Product);
}
_unitOfWork.Save();
return RedirectToAction(nameof(Index));
}
..
..
..
return View(productVM);
}
Upsert.cshtml
<section>
<div class="container-lg pt-4">
<form method="post" enctype="multipart/form-data">
<div class="row p-3 border">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="col-8 pt-4">
<div class="form-group row">
<div class="col-4">
<label asp-for="Product.Title"></label>
</div>
<div class="col-8">
<input asp-for="Product.Title" class="form-control" />
<span asp-validation-for="Product.Title" class="text-danger"></span>
</div>
</div>
<div class="form-group row">
<div class="col-4">
Image
</div>
<div class="col-8">
<input type="file" name="files" id="uploadBox" multiple class="form-control"
/>
</div>
</div>
<div class="form-group row">
<div class="col-8 offset-4">
#if (Model.Product.Id != 0)
{
<partial name="_EditAndBackToListButton" model="Model.Product.Id" />
}
else
{
<div class="row">
<div class="col">
<button type="submit" class="btn btn-primary form-control">Create</button>
</div>
<div class="col">
<button asp-action="Index" class="btn btn-success form-control">Back To List</button>
</div>
</div>
}
</div>
</div>
</div>
</div>
</form>
</div>
Is there any nuget package to compress the image size
You can check this demo, after testing, it can make 65KB image to 15KB image:
You just need to send source image location with the compress level 0~100, and where to send the
compressed image.
public IActionResult Index()
{
string filename = #"C:\*****\Saved Pictures\dog.jpg"; //source image location
Bitmap img = new Bitmap(filename); //image to bitmap
var name = "result.jpg"; //your output image name
Compress(img, #"C:\****\wwwroot\images\"+name, 0); //level 0~100, 0 is the worst resolution
return View();
}
public static void Compress(Bitmap srcBitMap, string destFile, long level)
{
Stream s = new FileStream(destFile, FileMode.Create); //create FileStream,this will finally be used to create the new image
Compress(srcBitMap, s, level); //main progress to compress image
s.Close();
}
private static ImageCodecInfo GetEncoderInfo(String mimeType)
{
int j;
ImageCodecInfo[] encoders;
encoders = ImageCodecInfo.GetImageEncoders();
for (j = 0; j < encoders.Length; ++j)
{
if (encoders[j].MimeType == mimeType)
return encoders[j];
}
return null;
}
private static void Compress(Bitmap srcBitmap, Stream destStream, long level)
{
ImageCodecInfo myImageCodecInfo;
Encoder myEncoder;
EncoderParameter myEncoderParameter;
EncoderParameters myEncoderParameters;
myImageCodecInfo = GetEncoderInfo("image/jpeg");
myEncoder = Encoder.Quality;
myEncoderParameters = new EncoderParameters(1);
myEncoderParameter = new EncoderParameter(myEncoder, level);
myEncoderParameters.Param[0] = myEncoderParameter;
srcBitmap.Save(destStream, myImageCodecInfo, myEncoderParameters);
}
Result(after vs before):
yes, you could use this: https://github.com/omuleanu/imager
so it would be something like this:
Image img = Imager.Resize(sourceImage, newWidth, maxHeight);
this is if you want to resize it on the server, after you uploaded it, but you could also resize it before the upload, as shown in this demo website:
https://prodinner.aspnetawesome.com/Meal
try to click edit on any item, after "change image"
for this cropper.js and dropzone.js are used.
https://fengyuanchen.github.io/cropperjs/
https://www.dropzonejs.com/
I am building a Xamarin Forms app, which needs to be able to print a nametag label on a label printer via Airprint on an iPad, but I am having problems with the image being blank when printing from a real device. The label is a fairly simple HTML string with a base64 encoded image embedded inside:
<html style="height: 100%">
<body style="height: 100%; margin: 0">
<div style="height: 100%; display: flex; flex-direction: column; justify-content: space-between">
<div style="display: flex; justify-content: space-between">
<span style="font-size: x-large">VISITOR</span>
<span><img src='data:image/jpeg;base64,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' /></span>
</div>
<div style="display:flex; flex-direction: column; align-items: center">
<span style="font-size: larger; font-weight: 600">[VisitorName]</span>
<span>[VisitorCompany]</span>
</div>
<div style="display: flex; justify-content: space-between">
<div style="display: flex; flex-direction: column">
<span>DATE OF VISIT</span>
<span>[CheckinTime]</span>
</div>
<div style="display: flex; flex-direction: column; align-items: flex-end">
<span>HOST</span>
<span>[HostName]</span>
</div>
</div>
</div>
</body>
</html>
My initial implementation works well on the iOS simulator, but on a real device, the image is not getting printed (it is just blank):
private static void PrintToPrinter(string label, UIPrinter selectedPrinter)
{
var printInfo = UIPrintInfo.PrintInfo;
printInfo.JobName = "My Job";
printInfo.OutputType = UIPrintInfoOutputType.General;
printInfo.Orientation = UIPrintInfoOrientation.Landscape;
var htmlFormatter = new UIMarkupTextPrintFormatter(label);
var printer = UIPrintInteractionController.SharedPrintController;
printer.PrintInfo = printInfo;
printer.PrintFormatter = htmlFormatter;
printer.PrintToPrinter(selectedPrinter, (printInteractionController, completed, error) =>
{
if (!completed && error != null)
{
Console.WriteLine($"Error: {error.LocalizedDescription ?? ""}");
}
});
printInfo.Dispose();
htmlFormatter.Dispose();
}
I read about other people having problems with images not getting printed, because the rendering apparently happens asynchronously: https://stackoverflow.com/a/40320983
So my next try was loading the markup into a web view, but I am not sure how to wait until it has loaded completely, so this is my naive try. It never moves past the while loop:
private static void PrintToPrinter(string label, UIPrinter selectedPrinter)
{
WkWebViewRenderer renderer = new WkWebViewRenderer();
renderer.LoadHtml(label, null);
while (renderer.IsLoading)
{
Thread.Sleep(100);
}
var printInfo = UIPrintInfo.PrintInfo;
printInfo.JobName = "My Job";
printInfo.OutputType = UIPrintInfoOutputType.General;
printInfo.Orientation = UIPrintInfoOrientation.Landscape;
var printer = UIPrintInteractionController.SharedPrintController;
printer.PrintInfo = printInfo;
printer.PrintFormatter = renderer.ViewPrintFormatter;
printer.PrintToPrinter(selectedPrinter, (printInteractionController, completed, error) =>
{
if (!completed && error != null)
{
Console.WriteLine($"Error: {error.LocalizedDescription ?? ""}");
}
});
printInfo.Dispose();
renderer.Dispose();
}
I then tried instantiating a WebView, but this just prints a blank page/label:
private static void PrintToPrinter(string label, UIPrinter selectedPrinter)
{
var view = new WebView
{
Source = new HtmlWebViewSource { Html = label }
};
var renderer = Platform.CreateRenderer(view);
var printInfo = UIPrintInfo.PrintInfo;
printInfo.JobName = "My Job";
printInfo.OutputType = UIPrintInfoOutputType.General;
printInfo.Orientation = UIPrintInfoOrientation.Landscape;
var printer = UIPrintInteractionController.SharedPrintController;
printer.PrintInfo = printInfo;
printer.PrintFormatter = renderer.NativeView.ViewPrintFormatter;
printer.PrintToPrinter(selectedPrinter, (printInteractionController, completed, error) =>
{
if (!completed && error != null)
{
Console.WriteLine($"Error: {error.LocalizedDescription ?? ""}");
}
});
printInfo.Dispose();
renderer.Dispose();
}
An attempt to find a way to wait for the view to be loaded, I tried this, but the Navigated event is never invoked:
private static void PrintToPrinter(string label, UIPrinter selectedPrinter)
{
var view = new WebView();
view.Navigated += (sender, e) =>
{
var renderer = Platform.CreateRenderer(view);
var printInfo = UIPrintInfo.PrintInfo;
printInfo.JobName = "My Job";
printInfo.OutputType = UIPrintInfoOutputType.General;
printInfo.Orientation = UIPrintInfoOrientation.Landscape;
var printer = UIPrintInteractionController.SharedPrintController;
printer.PrintInfo = printInfo;
printer.PrintFormatter = renderer.NativeView.ViewPrintFormatter;
printer.PrintToPrinter(selectedPrinter, (printInteractionController, completed, error) =>
{
if (!completed && error != null)
{
Console.WriteLine($"Error: {error.LocalizedDescription ?? ""}");
}
});
printInfo.Dispose();
renderer.Dispose();
};
view.Source = new HtmlWebViewSource { Html = label };
}
I am not sure what else to try, but I am open for any suggestions.
The View code is as follows:
#using Stimulsoft.Report.Mvc;
#using Stimulsoft.Report;
#{
ViewBag.Title = "ListPouyaProject";
Layout = "~/Views/Shared/_mainView.cshtml";
}
<section class="content">
<!-- Default box -->
<div class="box">
<div class="box-body">
<div class="form-group">
Start Date: <input type="text" id="date1" name="date1" onclick="PersianDatePicker.Show(this, '1392/03/22');" />
End Date : <input type="text" id="date2" name="date2" onclick="PersianDatePicker.Show(this, '1397/03/22');" />
</div>
<div class="form-group">
#Html.Stimulsoft().StiMvcViewer(new StiMvcViewerOptions()
{
Actions =
{
GetReport = "Report4_ListPouyaProject",
ViewerEvent = "ViewerEvent"
}
})
</div>
</div>
</div>
</section>
The Controller code is as follows:
public ActionResult Report4_ListPouyaProject()
{
var report = new StiReport();
report.Load(Server.MapPath("~/Content/Reports/ListPouyaProject.mrt"));
return StiMvcViewer.GetReportResult(report);
}
public ActionResult ListPouyaProject()
{
return View();
}
public ActionResult ViewerEvent()
{
return StiMvcViewer.ViewerEventResult();
}
I want to pass the date1 and date2 variables to the controller from view.
To do this, we need to add the following commands to the contoroller :
report.CompiledReport.DataSources["spm_report_4_ListPouyaProject"].Parameters["StartDate"].ParameterValue = DateTime.Parse(date1);
report.CompiledReport.DataSources["spm_report_4_ListPouyaProject"].Parameters["EndDate"].ParameterValue = DateTime.Parse(date2);
How to pass the parameters date1 and date2 from view to controller?
First, you need to add the StiMvcViewer component to the view page. Also, you need to pass the StiMvcViewerOptions object to the constructor. The minimum required options are two actions - GetReport and ViewerEvent, they are located in the Actions options group.
#using Stimulsoft.Report.MVC;
#Html.Stimulsoft().StiMvcViewer(new StiMvcViewerOptions()
{
Actions =
{
GetReport = "GetReport",
ViewerEvent = "ViewerEvent"
}
})
<div style="width: 150px;">
#Html.ActionLink("Simple List", "Index", new { id = "1" })
<br />Report Snapshot
</div>
and in controoller :
public ActionResult GetReport(int? id)
{
// Create the report object
StiReport report = new StiReport();
switch (id)
{
// Load report snapshot
case 1:
// Load report
// Load report snapshot
report.LoadDocument(Server.MapPath("~/Content/Reports/SimpleList.mdc"));
break;
}
// Load data from XML file for report template
if (!report.IsDocument)
{
DataSet data = new DataSet("Demo");
data.ReadXml(Server.MapPath("~/Content/Data/Demo.xml"));
report.RegData(data);
}
return StiMvcViewer.GetReportResult(report);
}
public ActionResult ViewerEvent()
{
return StiMvcViewer.ViewerEventResult();
}
I have a list of users and if you click on a user there has to be create a new message.
But every time I click on a user id is 0
I have this:
action method:
public ActionResult StuurBericht(int id = 0, string onderwerp = "")
{
using (var rep = new GebruikerRepository(Context.Klant.Id))
{
var model = PersoneelsDossierService.GetPersoneelsDossierMutatieModel(Context.Klant.Id, GetMutatieRol(), int.Parse(Context.Gebruiker.ExternId), Gebruiker.DienstverbandId, Gebruiker.DienstverbandId, "Functionarissen");
model.Functionarissen = PersoneelsDossierService.GetFunctionarissen(Context.Klant.Id, Gebruiker.DienstverbandId);
BeveiligingService.ControleerGebruikerVanKlant(Context.Klant.Id, Context.Gebruiker.Id);
if (id > 0)
{
ModelState.Clear();
var modelMessage = new Message();
modelMessage.GebruikerId = id;
modelMessage.Onderwerp = string.Format("RE: {0}", onderwerp);
return View(model);
}
}
return View();
}
and this is the view:
#model List<SDB.Models.Stamtabel>
#{
var ItemsByAccordatieFunctieGroep = Model.GroupBy(a => a.Code);
<div class="row">
#foreach (var Accordeerders in ItemsByAccordatieFunctieGroep)
{
<div class="col-md-4">
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading blue">#Accordeerders.Key</div>
<!-- List group -->
<ul class="list-group">
#foreach (var Accordeerder in Accordeerders)
{
<li class="list-group-item">
#Accordeerder.Omschrijving
</li>
}
</ul>
</div>
</div>
}
</div>
}
So my question is:
How to return the correct user and that you will get the correct id?
Thank you
this has to be the link for new message:
SelfService/Profiel/Nieuw?id=6240&onderwerp=test
So the controller is: Profiel.
But now the link is this:
/PersoneelsDossier/StuurBericht/0?onderwerp=HRM%20Management
So the controller link is incorrect.
Your #Url.Action is wrong, it should be:
#Accordeerder.Omschrijving
I have a model containing a boolean with no [Required] attribute
public bool IsOptedIn { get; set; }
I have overriden Object.cshtml as follows and am using #Html.EditorForModel() to generate my form
#{
var properties = ViewData.ModelMetadata.Properties
.Where(prop => prop.ShowForEdit && !ViewData.TemplateInfo.Visited(prop));
}
#foreach (var prop in properties)
{
var hasModelStateError = ViewContext.ViewData.ModelState.Any(m => m.Key == prop.PropertyName)
&& ViewContext.ViewData.ModelState[prop.PropertyName].Errors != null
&& ViewContext.ViewData.ModelState[prop.PropertyName].Errors.Count > 0;
<div class="control-group
#(hasModelStateError ? "error" : string.Empty)
#prop.PropertyName.ToLower()">
#if (prop.IsReadOnly)
{
<b>#prop.GetDisplayName()</b>
#Html.Display(prop.PropertyName)
}
else if (prop.HideSurroundingHtml)
{
#Html.Editor(prop.PropertyName)
}
else
{
<label class="control-label">
#prop.GetDisplayName()
#if (prop.IsRequired)
{
<span class="required">*</span>
}
</label>
<div class="controls">
#Html.Editor(prop.PropertyName)
#if (hasModelStateError)
{
<p class="alert alert-block">
#Html.ValidationMessage(prop.PropertyName)
</p>
}
#if (!string.IsNullOrWhiteSpace(prop.Description))
{
<p class="help-block">
#prop.Description
</p>
}
</div>
}
</div>
}
I am finding that bools in my model are always being marked as required. Why is this and how can I stop it happening?
DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false;
Add this line to your Application_Start method from Global.asax. By default MVC adds [Required] attribute to non-nullable value types (because you can't convert a null into a bool, it must be a bool).