Add a text above an image using mudbalzor - c#

I need to implement this kind of view using mudblazor image components:
I got the images correct, but how can I add text on top of the image like in the picture above? Here is my code:
<MudGrid>
u/foreach (var item in tabscatitm)
{
<MudItem xs="3" >
<MudImage Src=#item.ItemImageUrl Height="120" Width="120" u/onclick="ItemClick"
Class="rounded-lg"Style="border:thin;border-color:darkgray;border-radius:10px"/>
<MudText Typo="Typo.h6">#item.ItemName</MudText>
<MudText Typo="Typo.body2">#item.ItemShortName</MudText>
</MudItem>
}
</MudGrid>

You could try something like this:
CustomImage.razor
<div style="position: relative; cursor: pointer" #onclick="#this.Clicked">
#* the image *#
<MudImage Src="#this.Src" Alt="#this.Alt"/>
#* A transparent background for the text *#
<MudPaper
Elevation="0"
Class="rounded-0"
Style="position: absolute; bottom: 0; left: 0; padding: 25px; width: 100vw; opacity: 0.6">
#* Hack: the text is needed to give the transparent part the proper height, but will also display the text in 0.6 transparency *#
<MudText Typo="Typo.h4">#this.Name</MudText>
<MudText Typo="Typo.h6">#this.ShortName</MudText>
</MudPaper>
#* The text (in black) *#
<span style="position: absolute; bottom: 0; left: 0; padding: 25px; width: 100vw; color: black">
<MudText Typo="Typo.h4">#this.Name</MudText>
<MudText Typo="Typo.h6">#this.ShortName</MudText>
</span>
</div>
#code {
[Parameter]
public string Src { get; set; } = default!;
[Parameter]
public string Alt { get; set; } = string.Empty;
[Parameter]
public string Name { get; set; } = default!;
[Parameter]
public string ShortName { get; set; } = default!;
[Parameter]
public EventCallback OnClick { get; set; }
private async Task Clicked()
{
await this.OnClick.InvokeAsync();
}
}
If used like this:
<MudGrid>
#foreach (var item in this.items)
{
<MudItem md="6">
<CustomImage
Src="#item.Src"
Name="#item.Name"
ShortName="#item.ShortName"
OnClick="#(() => this.Click(item))"/>
</MudItem>
}
</MudGrid>
#code {
private readonly List<Item> items = new()
{
new Item { Name = "A tasty burger", ShortName = "Some more text or a price...", Src = "images/hamburger.jpg" },
new Item { Name = "Another burger", ShortName = "...", Src = "images/hamburger.jpg" }
};
private void Click(Item item)
{
Console.WriteLine($"Clicked {item.Name}");
}
}
The result will be:
Note the comment about the hack: my CSS knowledge is rather limited, but I'm sure some colleague of yours can help with that. This is just a quick demo for you to get started.

Related

Page not redirecting as intended

I am trying to validate input data and if the validation is true, go to the next view. However, I can see my code is not existing the present page.
Controller:
public ActionResult ForgotLoginId()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ForgotLoginId(Testing ForgotLIDAuthentication)
{
if (ModelState.IsValid)
{
using(SUPRTestingDBEntities2 db = new SUPRTestingDBEntities2())
{
if (obj != null)
{
Session["LoginID"] = obj.EmailID.ToString();
Session["EmailID"] = obj.TaxID.ToString();
return RedirectToAction("LIDAuthentication");
}
else if (obj == null)
{
return View();
}
}
}
return View();
}
public ActionResult LIDAuthentication()
{
if (Testing.IsUserValid())
{
return View();
}
else
{
return RedirectToAction("ForgotLoginID");
}
}
View:
#using (Html.BeginForm("ForgotLoginID", "Corporation", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
if (ViewBag.Message != null)
{
<div style="border: 1px solid red">
#ViewBag.Message
</div>
}
<div>
<textarea name="EmailId" style="width:50%; border-color: black" required>#Html.TextAreaFor(a => a.EmailID)</textarea>
<text>#Html.ValidationMessageFor(a => a.EmailID)</text>
<textarea name="TaxID" style="width:30%; border-color: black" required placeholder="xxxxxxxxx" maxlength="9">#Html.TextAreaFor(a => a.Password)</textarea>
<text>#Html.ValidationMessageFor(a => a.Password)</text>
<button type="submit" style="width: 20%; color: white; background-color: deepskyblue; border-color:black" value="SubmitRequest" name="Submitbtn"><b>Submit</b></button>
</div>
}
Model:
public partial class Testing
{
public int LoginID { get; set; }
[Required]
public string EmailID { get; set; }
[Required]
public int TaxID { get; set; }
public string Password { get; set; }
[Required]
public string CorporationName { get; set; }
public static bool IsUserValid()
{
HttpContext context = HttpContext.Current;
if (context.Session["LoginID"] != null)
{
return true;
}
else
return false;
}
}
Here, the page not exiting the current view. When I tried to insert breakpoint after [httppost] in Controller, it is not even hitting the code.
Also, I am not sure why my view has something in fields, I don't know what it is. Not sure if this is affecting my output.
ForgotLoginID View
Please help me with this.
I'm not sure if this will fix your problem, but your view code should look something like:
#using (Html.BeginForm("ForgotLoginID", "Corporation", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
if (ViewBag.Message != null)
{
<div style="border: 1px solid red">
#ViewBag.Message
</div>
}
#Html.TextAreaFor(a => a.EmailID)
#Html.ValidationMessageFor(a => a.EmailID)
#Html.TextAreaFor(a => a.Password)
#Html.ValidationMessageFor(a => a.Password)
<button type="submit" style="width: 20%; color: white; background-color: deepskyblue; border-color:black" value="SubmitRequest" name="Submitbtn"><b>Submit</b></button>
}

Blazor - show confirmation dialog before delete/update?

In the following Blazor (server-side) code snips. How to prompt the confirmation dialog?
<tbody>
#foreach (var r in lists)
{
var s = r.ID;
<tr>
<td>#s</td>
<td><button class="btn btn-primary" #onclick="() => DeleteSymbol(s)">Delete</button></td>
</tr>
}
</tbody>
#code {
async Task DeleteSymbol(string id)
{
// Get confirmation response from user before running deletion?
// Delete!
}
}
#inject IJSRuntime JsRuntime
<tbody>
...
</tbody>
#code {
async Task DeleteSymbol(string id)
{
bool confirmed = await JsRuntime.InvokeAsync<bool>("confirm", "Are you sure?");
if (confirmed)
{
// Delete!
}
}
}
I created a simple popconfirm component.
<div class="pop-container">
#if (Show)
{
<div class="popconfirm">
#Message
<button type="button" class="btn btn-warning" #onclick="() => Confirmation(false)">No</button>
<button type="button" class="btn btn-primary" #onclick="() => Confirmation(true)">Yes</button>
</div>
}
<button type="button" class="#Class" #onclick="ShowPop">#Title</button>
</div>
#code {
public bool Show { get; set; }
[Parameter] public string Title { get; set; } = "Delete";
[Parameter] public string Class { get; set; } = "btn btn-danger";
[Parameter] public string Message { get; set; } = "Are you sure?";
[Parameter] public EventCallback<bool> ConfirmedChanged { get; set; }
public async Task Confirmation(bool value)
{
Show = false;
await ConfirmedChanged.InvokeAsync(value);
}
public void ShowPop()
{
Show = true;
}
}
CSS
.pop-container{
position: relative;
}
.popconfirm{
background-color: white;
border-style: solid;
border-width: 1px;
border-color: lightblue;
width: 250px;
position: absolute;
top: -50px;
padding: 10px;
border-radius: 8px;
}
Usage
<Popconfirm ConfirmedChanged="Test" />
#code{
public void Test(bool test)
{
Console.WriteLine(test);
}
}
I used #Egill's answer and extended it a bit more to fit my requirement.
This is what I needed.
The delete button as an image. (using ion-icons)
The background disabled
Popup in center of the screen.
Using RenderFragments for the particular requirements.
This is what I ended up with.
Component:
<div>
<button type="button" class="#Class" #onclick="ShowPop">
#if (ButtonContent != null)
{
#ButtonContent
}
else
{
#Title
}
</button>
#if (Show)
{
<div class="pop-container">
<div class="popconfirm gradient-to-gray">
<div class="row">
<div class="col">
#if (MessageTemplate != null)
{
#MessageTemplate
}
else
{
#Message
}
</div>
</div>
<div class="row">
<div class="col-6">
<button type="button" class="btn #YesCssClass" #onclick="() => Confirmation(true)">Yes</button>
</div>
<div class="col-6">
<button type="button" class="btn #NoCssClass" #onclick="() => Confirmation(false)">No</button>
</div>
</div>
</div>
</div>
}
</div>
#code {
public class MessageTemp
{
public string msg { get; set; }
}
public bool Show { get; set; }
[Parameter]
public string Title { get; set; } = "Delete";
[Parameter]
public string Class { get; set; } = "btn btn-danger";
[Parameter]
public string YesCssClass { get; set; } = "btn btn-success";
[Parameter]
public string NoCssClass { get; set; } = "btn btn-warning";
[Parameter]
public string Message { get; set; } = "Are you sure?";
[Parameter]
public EventCallback<bool> ConfirmedChanged { get; set; }
[Parameter]
public RenderFragment ButtonContent { get; set; }
[Parameter]
public RenderFragment MessageTemplate { get; set; }
public async Task Confirmation(bool value)
{
Show = false;
await ConfirmedChanged.InvokeAsync(value);
}
public void ShowPop()
{
Show = true;
}
}
CSS:
.pop-container {
z-index: 1000;
width: 100vw;
height: 100vh;
position: fixed;
top: 0px;
left: 0px;
background-color: rgba(132, 4, 4, 0.77);
}
.popconfirm {
color: white;
background-color: gray;
border-style: solid;
border-width: 1px;
border-color: lightblue;
padding: 10px;
border-radius: 15px;
min-width: 250px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.pop-message-card, .pop-message-card div {
background-color: rgba(0, 0, 0, 0.00)
}
.gradient-to-gray {
background: linear-gradient(0deg,#000000,#888);
color: #fff;
}
.btn-yes {
background-color: darkgreen;
color: limegreen;
width: 100%;
}
.btn-no {
background-color: darkred;
color: lightcoral;
width: 100%;
}
Usage:
<ConfirmPopup Title="Delete Unit"
Message="Delete record?"
Class="btn btn-psl-delete"
YesCssClass="btn-yes"
NoCssClass="btn-no"
ConfirmedChanged="DeleteRecord">
<ButtonContent>
<ion-icon name="trash-outline" style="font-size:2em;color:white;"></ion-icon>
</ButtonContent>
<MessageTemplate>
<div class="card pop-message-card">
<div class="card-header">
Delete Record?
</div>
<div class="card-body">
<p>
This will delete the record!<br />
Are you sure?
</p>
</div>
</div>
</MessageTemplate>
</ConfirmPopup>
<button title="Delete" type="button" #onclick="#(async () => await delete(id))" class="m-0 btn btn-outline-secondary">
async Task delete(string id)
{
bool confirmed = await js.InvokeAsync<bool>
("confirm", "Delete?");
if (confirmed)
{
// delete
}
}

.NET MVC - Map text from TextBoxFor to an Array

My model is set up so the controller can deliver a Json response of data from user input. The 'rgba' property of my model is an array of ints. If a user enters text of say '255, 0, 0, 1' into the TextBoxFor for 'rgba', the text is not mapping into an array (which I thought was supposed to happen automagically). Instead, int[0] is what makes it to the controller.
I've tried all the potential solutions I could find on here, including passing a FormCollection object to controller. I've tried to get the TextBoxFor value using JS/jQuery and manipulate the data, but can't figure out how to pass the manipulated data back to the model (this seems less than ideal, like there should be an easy way to do this in .Net).
Controller:
public class HomeController : Controller
{
public IActionResult NewColor()
{
Rootobject newColor = new Rootobject();
return View(newColor);
}
[HttpPost]
public IActionResult NewColor(Rootobject color)
{
var json = JsonConvert.SerializeObject(color);
return Json(json);
}
}
Model:
public class Rootobject
{
public Color[] colors { get; set; }
}
public class Color
{
public string color { get; set; }
public string category { get; set; }
public string type { get; set; }
public Code code { get; set; }
}
public class Code
{
public int[] rgba { get; set; }
public string hex { get; set; }
}
View:
#model WebAppPlayground.Models.Rootobject
#{
ViewData["Title"] = "New Color";
}
<style>
input:focus {
border-radius: 5px;
}
input {
padding: 2px;
border-radius: 5px;
border-style: ridge;
}
</style>
<h2>New Color</h2>
<h4>Color</h4>
<hr />
<center>
#using (Html.BeginForm("NewColor", "Home", FormMethod.Post, new { id = "form" }))
{
<table style="border-collapse:separate; border-spacing: 5px 5px">
<tbody>
<tr class="form-group" for="Color">
<td>Color</td>
<td>#Html.TextBoxFor(m => m.colors[0].color)</td>
</tr>
<tr class="form-group">
<td class="">Category</td>
<td>#Html.TextBoxFor(m => m.colors[0].category)</td>
</tr>
<tr class="form-group">
<td class="">Type</td>
<td>#Html.TextBoxFor(m => m.colors[0].type)</td>
</tr>
<tr class="form-group">
<td class="">rgba</td>
<td>#Html.TextBoxFor(m => m.colors[0].code.rgba, new { id = "rgba"})</td>
</tr>
<tr class="form-group">
<td class="">Hex</td>
<td>#Html.TextBoxFor(m => m.colors[0].code.hex)</td>
</tr>
</tbody>
</table>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
}
<div>
<a asp-action="Index">Back to List</a>
</div>
</center>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
I would like my Controller to receive an array of Int's via rgba TextBoxFor where user enters text eg 255, 0, 0, 1.
I think there is something (obvious) I am overlooking or not understanding.
****Updated controller post method to remove added 'rgba_str' prop per #i_ll_be_back 's answer and deliver desired Json data:
[HttpPost]
public IActionResult NewColor(Rootobject color)
{
var json = JsonConvert.SerializeObject(color);
JObject jsonObject = JObject.Parse(json);
JObject code = (JObject)jsonObject["colors"][0]["code"];
code.Property("rgba_str").Remove();
return Json(jsonObject);
}
Try the following reshaping the Code class in the following:
public class Code
{
public int[] rgba { get; set; }
public string rgba_str
{
get
{
var res = "";
for(int i = 0; i < rgba.Length; i++) {
res += rgba[i] + (i != rgba.Length - 1 ? "," : "");
}
return res;
}
set
{
var strArray = value.Split(',');
rgba = new int[strArray.Length];
for(int i = 0; i < strArray.Length; i++) {
rgba[i] = int.Parse(strArray[i]);
}
}
}
public string hex { get; set; }
}
Now bind the TextBoxFor with the rgba_str field

iframe and image container code improvements MVC 5

As a part of my first project i had to implement image and video display in views using MVC 5. It is working and i'm quite happy with the achievement, however there are some improvements had to be made, how would you recommend to improve following code:
to display a picture i used this code:
<div style="float: none; margin: auto auto 30px auto ;height: 280px; width: 400px; ">
<div id="imageContainer">
<img src="~/Images/#Html.Raw(Model.ImagePath)" />
</div>
</div>
in a case when i don't provide a path, this container stays empty but still visible as empty block on the page with image icon, indicating that image suppose to be here... How i can get rid of it, if i dont want picture to be shown in this view?
The same situation with video container, i simply using embeded youtube link as a Videolink for video:
<iframe src=#Html.Raw(Model.VideoLink) width="560" height="315" frameborder="1" align="middle" display:block style="margin:auto auto 30px auto"></iframe>
If i provide Videolink - it works fine, if i dont, i have a container showing an error. Is there a way to simply hide those containers if there are no input made?
i'll show you the way i set it all up, it all in one class Media
public class Media
{
public int Id { get; set; }
public string title { get; set; }
[Required, StringLength(512)]
public string description { get; set; }
[Required]
[AllowHtml]
public string body { get; set; }
public string ImagePath { get; set; }
public string VideoLink { get; set; }
public string Source { get; set; }
public TagsEnum TagsEnum { get; set; }
[Column(TypeName = "datetime2")]
[DisplayFormat(DataFormatString = "{0:dd MMMM , yyyy HH:mm}", ApplyFormatInEditMode = true)]
public DateTime? NewsDate { get; set; }
}
Update
this is what i think it should look like using Sam's advise, right? If so, it is still not giving right result
#if (!string.IsNullOrEmpty(b.ImagePath))
{
}
else
{
<div style="float: right; margin: 10px 10px 25px 25px; height: 100px; width:150px">
<div id="imageContainer">
<img src="~/Images/#Html.Raw(b.ImagePath))" style='height: 100%; width: 100%;' />
</div>
</div>
}
#if (!string.IsNullOrEmpty(b.ImagePath)) {
#<div style="float: right; margin: 10px 10px 25px 25px; height: 100px; width:150px">
<div id="imageContainer">
<img src="~/Images/#Html.Raw(b.ImagePath))" style='height: 100%; width: 100%;' />
</div>
</div>
}

Keep ASP MVC FormCollection values after posting back from controller

On my Index view, I need to allow the user to enter search criteria. This criteria is sent back to the Index controller in the form of a FormCollection object. I then extract the search criteria, pull the requested information from the database, and send the user back to the Index view. However, once the user get's back to the index view with the requested info, the data from the FormCollection object is now blank.
I would like to be able to keep the user's search criteria in the three text boxes that I use, however I'm not sure how using a FormCollection. Does anyone know either how to do this or another I should use?
View
#using(Html.BeginForm())
{
<div id="searchBox" class="boxMe">
<div id="zipBox" style="float: left; padding-left: 20px; padding-right: 10px; padding-top: 20px; padding-bottom: 20px; vertical-align: top;">
#Html.Raw("Zip Code")
#Html.TextArea("ZipSearch", new { style = "width: 300px;", placeholder = "Enter up to 35 comma separated zip codes" })
</div>
<div id="dateBox" style="float: left; padding-right: 10px; padding-top: 20px;">
#Html.Raw("Effective on this date")
#Html.TextBox("DateSearch", null, new { style="width: 80px;"})
</div>
<div id="stateBox" style="float: left; padding-right: 20px; padding-top: 20px;">
#Html.Raw("State")
#Html.TextBox("StateSearch", null, new { style = "width: 25px;" })
<button type="submit">Search</button>
</div>
</div>
<div style="clear: both;"></div>
}
Controller
public ViewResult Index(FormCollection searchDetails = null)
{
string zip = searchDetails["ZipSearch"];
string date = searchDetails["DateSearch"];
string state = searchDetails["StateSearch"];
if (String.IsNullOrWhiteSpace(zip) && String.IsNullOrWhiteSpace(date) && String.IsNullOrWhiteSpace(state))
{
return View();
}
string[] zipArray;
DateTime effectiveDate;
//Convert date string to DateTime type
if (String.IsNullOrWhiteSpace(date))
{
effectiveDate = DateTime.MinValue;
}
else
{
effectiveDate = Convert.ToDateTime(date);
}
//Conduct search based on Zip Codes
if (!String.IsNullOrWhiteSpace(zip))
{
//Create array and remove white spaces
zipArray = zip.Split(',').Distinct().ToArray();
for (int i = 0; i < zipArray.Length; i++)
{
zipArray[i] = zipArray[i].Trim();
}
//Add zip codes to list object then send back to view
List<ZipCodeTerritory> zips = new List<ZipCodeTerritory>();
if (String.IsNullOrWhiteSpace(state))
{
foreach (var items in zipArray)
{
var item = from z in db.ZipCodeTerritory
where z.ZipCode.Equals(items) &&
z.EffectiveDate >= effectiveDate
select z;
zips.AddRange(item);
}
}
else
{
foreach (var items in zipArray)
{
var item = from z in db.ZipCodeTerritory
where z.ZipCode.Equals(items) &&
z.EffectiveDate >= effectiveDate
select z;
zips.AddRange(item);
}
}
return View(zips);
}
//Zip code was not specified so search by state
if (!String.IsNullOrWhiteSpace(state))
{
var items = from z in db.ZipCodeTerritory
where z.StateCode.Equals(state) &&
z.EffectiveDate >= effectiveDate
select z;
return View(items);
}
//Neither zip code or state specified, simply search by date
var dateOnly = from z in db.ZipCodeTerritory
where z.EffectiveDate >= effectiveDate
select z;
return View(dateOnly);
}
EDIT
Following the instructions below I've created my View model like so:
public class ZipCodeIndex
{
public List<ZipCodeTerritory> zipCodeTerritory { get; set; }
public string searchZip { get; set; }
public string searchDate { get; set; }
public string searchState { get; set; }
}
However in my View I cannot access any of these properties. The header for the view is written like this:
#model IEnumerable<Monet.ViewModel.ZipCodeIndex>
However all the TextBoxFor and the TextAreaFor helpers say none of the specified properties exist.
#using(Html.BeginForm("Index", "ZipCodeTerritory", FormMethod.Post))
{
<div id="searchBox" class="boxMe">
<div id="zipBox" style="float: left; padding-left: 20px; padding-right: 10px; padding-top: 20px; padding-bottom: 20px; vertical-align: top;">
#Html.Raw("Zip Code")
#Html.TextAreaFor(model => model.searchZip, new { style = "width: 300px;", placeholder = "Enter up to 35 comma separated zip codes" })
</div>
<div id="dateBox" style="float: left; padding-right: 10px; padding-top: 20px;">
#Html.Raw("Effective on this date")
#Html.TextBoxFor(model => model.searchDate, null, new { style="width: 80px;"})
</div>
<div id="stateBox" style="float: left; padding-right: 20px; padding-top: 20px;">
#Html.Raw("State")
#Html.TextBoxFor(model => model.searchState, null, new { style = "width: 25px;" })
<button type="submit">Search</button>
</div>
</div>
<div style="clear: both;"></div>
}
FINAL EDIT
Missed that the page was looking for an IEnuerable Model object. Changed the header to this and fixed the problem.
#model Monet.ViewModel.ZipCodeIndex
Ok so the first thing you want to do is create a model just a new class file that looks something like this.
public class AddressModel
{
public int zip{ get; set; }
public string state{ get; set; }
public string date{ get; set; }
}
Then another new class file called a view model something like this. This way you can reference it for different things. Have like your search address then return the results in seperate list.
public class AddressViewModel
{
public AddressModel SearchAddress { get; set; }
public List<AddressModel> ResultsAddress{ get; set; }
}
Then in your view you reference the viewmodel like this #model MVCTestProject.ViewModels.InsertPage
on the top line.
Then these will be your text boxes.
#using (Html.BeginForm("Submit", "Insert", FormMethod.Post))
{
#Html.TextBoxFor(model => model.SearchAddress.zip)
#Html.TextBoxFor(model => model.SearchAddress.state)
#Html.TextBoxFor(model => model.SearchAddress.date)
<input id="button" name="button" type="submit" value="submit" />
}
And in your controller it will be similar to this.
public ActionResult Submit(AddressViewModel model)
{
model.ResultsAddress = (from z in db.ZipCodeTerritory
where z.StateCode.Equals(state) &&
z.EffectiveDate >= model.SearchAddress.date
select new AddressModel {
date = z.effectiveDate }).toList();
return View("viewName", model);
}
This will return your original Search criteria and the results. Is probably not all a hundred percent functional but the main ideas are there, and I can help you through problems if you decide to go down this path.
to display the results
#for (int i = 0; i < Model.ResultsAddress.Count; i++)
{
#Html.DisplayFor(model => model.ResultsAddress[i].date)
}
or for just displaying them top one can be used for editing and resubmitting the data.
#foreach (var item in Model.ResultsAddress)
{
<div>#item.date</div>
}
Ryan Schlueter is right that you should use ASP.NET MVC principles such as model, autobinding to model fields in action parameters and so on. But the main in this case you should use strongly typed view:
Action:
public ActionResult SomeAction(ModelClass model)
{
....
return("ViewName", model)
}
View:
#model SomeNamespace.ModelClass
#using (Html.BeginForm("Submit", "Insert", FormMethod.Post))
{
#Html.TextBoxFor(model => model.Property1)
#Html.TextBoxFor(model => model.Property2)
#Html.TextBoxFor(model => model.Property3)
<input id="button" name="button" type="submit" value="submit" />
}

Categories

Resources